• Cocos Creator TypeScript 套牛游戏


    视频操作地址:https://www.bilibili.com/video/BV1NN4y1V7Jy/?vd_source=0a0b591838e1a4cda6ec55da0e1c9019

    源码地址:https://gitee.com/OnlyOneW/cocos-study/tree/master/cocos-cow

    0 前提

    cococs官方脚本指南
    已安装Cocos Creator 3.5.2

    1 新建2D项目

    2 新建文件夹

    在资源管理器中新建文件夹用来保存对应的资源

    • scenes : 保存场景
    • scripts : 保存脚本资源
    • res : 保存图片等资源
    • framework : 保存自定义类文件
    • clips : 保存动画文件
    • prefacs : 保存自定义预制文件

    3 拖入图片等资源至res文件夹

    会提供资源文件:res下的res-cow

    4 搭建主页面

      1. 项目,项目设置里,调整尺寸为竖屏 (适配屏幕高度):640*960,
      1. 选中层级管理器,保存场景至,scenes文件夹下,命名:Main (ctrl+s 保存)
      1. 搭建背景
        新建2D对象,sprite(精灵):spt_bg
        设置背景图、尺寸:640*960 (默认的)
      1. 搭建按钮
        在spt_bg下,新建UI组件,Button(按钮):btn_capture
        删除btn_capture下的Label
        设置4种状态下的图片:初始、按下,设置给的图片,其余2个删掉
        设置大小:173*129
        拖动到合适位置
      1. 搭建牛儿
        把第一张牛儿图片拖到层级管理器spt_bg下,重命名为:cow

    5 奔跑的牛

    5.1 代码实现

    在framework新建TypeScript,命名:CowSkin

    1. 牛的皮肤类
    import { _decorator, Component, Node } from 'cc';
    const { ccclass, property } = _decorator;
    
    @ccclass('CowSkin')
    export class CowSkin  {
        
    	@property(cc.SpriteFrame)
    	public cows : [cc.SpriteFrame] = [];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在scripts新建TypeScript,命名:cow
    2. 牛的类型数组

    import { CowSkin } from '../framework/CowSkin';
    
    • 1
    @property(CowSkin)
    public cow_sets : [CowSkin] = [];
    
    
    private _intervalTime = 0;
    private _randomType = Math.floor(Math.random()*3);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. update回调函数实现帧动画,更换皮肤
    update(deltaTime: number) {
    		let dt = deltaTime ;
            // 间隔时间
            this._intervalTime += dt;
            let index = Math.floor(this._intervalTime / 0.2);
            index = index%3;
            // 获取一种牛的类型
            let cowSet = this.cow_sets[this._randomType];
            
            // 获取精灵组件
            let sprite = this.node.getComponent(cc.Sprite);
            sprite.spriteFrame = cowSet.cows[index];
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5.2 定义一个数组实现奔跑动画

    在clips 文件夹下新建动画剪辑:runClip,层级管理器,选中cow,选择动画编辑器,关联新建的runClip文件,编辑动画,设置播放模式,加入尾帧事件,runCallback

    cow.ts加入下面函数:

    runCallback() {
    	    cc.log("一个轮回结束!");
    	    this._randomType = Math.floor(Math.random()*3);
    	}
    
    • 1
    • 2
    • 3
    • 4

    选择Play on load

    保存

    退出动画编辑

    5.3 属性检查器按顺序拖入图片

    层级管理器,选中cow,关联cow.ts,设置参数,并按顺序拖入图片

    5.4 将整个cow节点作为预制体

    把cow节点拖到prefabs文件夹下,删除cow节点,把cow预制节点,拖到spt_bg下

    选择Play on load

    至此随机不同种的牛奔跑,已实现

    6 新建game.ts

    新建game.ts当做全局脚本文件,挂载到Canvas

    7 套绳

    拖动套绳图片:rope,至背景图片spt_bg下,默认不显示

    7.1 改锚点

      1. 改预制体cow的锚点

    选中,选矩形变化,移至牛头下,用于方便判断牛头是否套住,删除原来的cow,重新拖一下

      1. 调整绳子的锚点至,圆圈中央

    7.2 game.ts新建属性

    @property(cc.Node)
    public rope_node : cc.Node = null;
    
    
    @property(cc.Node)
    public cow_ins : cc.Node =null;
    
    
    @property(cc.SpriteFrame)
    public rope_imgs : [cc.SpriteFrame] = [];
    
    
    @property(cc.Prefab)
    public cow_prefab : cc.Prefab = null;
    
    @property
    public time  = 0;
    
    private _mainScene = 'Main';
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    并在Canvas里绑定

    • rope_node(rope)
    • cow_ins(cow)
    • rope_imgs(4张绳子图片按顺序拖动)
    • cow_prefab(cow预制体)
    • time(60)

    保存

    注:此处容易忽略初始化绑定

    7.3 按钮事件绑定代码,

    start() {
    		
    		let btn_capture_node = cc.find("Canvas/spt_bg/btn_capture");
    				
    		btn_capture_node.on(NodeEventType.TOUCH_START, this._touchStart_btn_capture, this);
    		
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    并定义

    _touchStart_btn_capture (touch: Touch, event: EventTouch) {
    		
    		
     }
    
    • 1
    • 2
    • 3
    • 4

    具体请见最后

    8 计分器和倒计时

    8.1 第一步:界面添加Label

    • 分数显示
      在spt_bg下,新建2D对象,Label(文本):lb_score,来显示得分
      设置初始值 : Score:0
      设置大小 : 50
      拖动到合适位置

    • 倒计时
      在spt_bg下,新建2D对象,Label(文本):lb_count_down,来显示倒计时
      设置初始参数:60s
      设置大小 : 50
      并添加位图字体样式和字体颜色:#EB78E6
      拖动到合适位置

    8.2 第二步:用位图字体让显示更美观

    去掉勾选:UseSystemFont
    拖入已备好的字体资源,至Font

    8.3 第三步:编写逻辑代码

    此步片段代码可忽略,直接查看总代码

    • 分数显示
    start () {
            // 定义初始化得分为0
            this.scoreNum = 0;
    }
        
    // 捕捉成功,分数+1
    this.scoreNum++;
    let lb_score = cc.find("Canvas/spt_bg/lb_score").getComponent(cc.Label);
    lb_score.string = "Score: " + this.scoreNum;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 倒计时
    start () {
        // 获得计时器组件
        let countDownLabel = cc.find("Canvas/spt_bg/lb_count_down").getComponent(cc.Label);
        let time = 60;
        // 倒计时
        this.schedule(function () {
            time--;
            countDownLabel.string = "Time: " + time + " s";
        },1);
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    9 背景音效

    9.1 导入模块

    import { AudioSource, assert} from 'cc';
    
    • 1

    9.2 添加组件:AudioSource

    Canvas里,添加组件 按钮,选择 Audio -> AudioSource

    设置文件
    设置播放方式

    9.3 属性

    @property(cc.AudioClip)
    public audio_clip : cc.AudioClip = null;
    	
    @property(AudioSource)
    public audioSource: AudioSource = null!;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Canvas里绑定属性 audio_clip

    9.4 初始化audioSource属性

    start() {
    	// 获取 AudioSource 组件
    	const audioSource_set = this.node.getComponent(AudioSource)!;
    	// 检查是否含有 AudioSource,如果没有,则输出错误消息
    	assert(audioSource_set);
    	// 将组件赋到全局变量 audioSource 中
    	this.audioSource = audioSource_set;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    9.5 播放套中音效

    // 音效
    this.audioSource.playOneShot(this.audio_clip, 1);
    
    • 1
    • 2

    10 粒子

    10.1 导入模块

    import { ParticleSystem2D} from 'cc';
    
    • 1

    10.2 新建ParticleSystem2D(粒子)

    在rope下,新建2D对象,ParticleSystem2D(粒子):pc2d_fireworks

    10.3 设置粒子参数

    不勾选 PlayOnLoad
    勾选 Custom
    设置 SpriteFrame

    10.4 显示粒子效果

    套中牛,显示粒子效果

    // 粒子
    let pc2d_fireworksNode = cc.find("Canvas/spt_bg/rope/pc2d_fireworks");
    this.fireworks = pc2d_fireworksNode.getComponent(ParticleSystem2D)
    this.fireworks.resetSystem()
    
    • 1
    • 2
    • 3
    • 4

    11 最终成就系统

    倒计时为0时游戏结束,并根据玩家的最终得分显示成就;比如小于等于3分就是套牛青铜,大于三分小于6分就是套牛高手,大于6分以上就是套牛王者。

    11.1 制作一个显示用的弹窗

    包含一个关闭按钮,标题和称号。

    • spt_result
      Canvas下,新建2D对象,sprite(精灵):spt_result
      设置背景图、尺寸、九宫格(Sliced)模式,
      (九宫格编辑得选中图片去编辑)
      默认不显示

    • close
      拖入关闭按钮至spt_result 背景下,,命名为close
      调整参数
      调整位置:182 182

    • lb_title
      在spt_result下,新建2D对象,Label(文本):lb_title,来显示等级
      设置初始参数:60 #252323
      调整位置

    • lb_content
      在spt_result下,新建2D对象,Label(文本):lb_content,来显示分数
      设置初始参数:80 #252323
      调整位置

    11.2 最终代码

    11.2.1 game.ts导入模块

    import { _decorator, Component, Node ,Vec3 , NodeEventType,Tween, AudioSource, assert,ParticleSystem2D} from 'cc';
    
    • 1

    11.2.2 套绳逻辑代码

    _touchStart_btn_capture (touch: Touch, event: EventTouch) {
    		let bgNode = touch.currentTarget._parent;
    		let currentTarget = touch.currentTarget;
    		this.rope_node.active = true;
    		// 设置绳子在当前父节点的顺序
    		this.rope_node.setSiblingIndex(100);
    		let rope_node_p = this.rope_node.getPosition();
    		let rope_node_y_start = - 600 ;
    		let rope_node_y_up = 115 ;
    		let rope_node_y_down = -600 ;
    		// 设置绳子起始位置
    		this.rope_node.setPosition(rope_node_p.x,rope_node_y_start);
    		rope_node_p = this.rope_node.getPosition();
    		let that = this ;
    		let tao_num: number = 100;
    		let tween_node :cc.Node = this.rope_node ;
    		let tweenDuration: number = 0.5;
    		let t1 = cc.tween(tween_node)
    		    .to(tweenDuration, { position: new Vec3(rope_node_p.x,rope_node_y_up,0) }
    				,{          
    					onUpdate:(target:Vec3, ratio:number)=>{
    						this.rope_node = target;
    					}
    				}
    			)
    			// to 动作完成后会调用该方法     
    			.call( ()=>{
    				// 获取当前牛儿的x点
    				let cow_ins_p = that.cow_ins.getPosition()
    				let currentX = cow_ins_p.x;
    				if (currentX > -tao_num && currentX < tao_num){
    				    cc.log("捕捉成功!");
    					
    					// 音效
    					this.audioSource.playOneShot(this.audio_clip, 1);
    					
    					// 粒子
    					let pc2d_fireworksNode = cc.find("Canvas/spt_bg/rope/pc2d_fireworks");
    					this.fireworks = pc2d_fireworksNode.getComponent(ParticleSystem2D)
    					this.fireworks.resetSystem()
    					
    				    // 移除
    				    bgNode.removeChild(that.cow_ins);
    				    // 获取牛儿的类型
    					let cowType = that.cow_ins.getComponent("cow")
    					
    				    let ropeType = cowType._randomType +1;
    					
    					that.rope_node.getComponent(cc.Sprite).spriteFrame = that.rope_imgs[ropeType];
    					
    				    // 生成新的牛节点
    				    that.cow_ins = cc.instantiate(that.cow_prefab);
    					that.cow_ins.setPosition(cow_ins_p.x,0);
    					
    				    bgNode.addChild(that.cow_ins);
    				    //
    				    that.success = true;
    				    // 分数+1
    				    that.scoreNum ++;
    				} else {
    				    cc.log("捕捉失败!")
    				}
    			})
    		let t2 = cc.tween(tween_node)
    		    .to(tweenDuration, { position: new Vec3(rope_node_p.x,rope_node_y_down,0) }
    				,{
    					onUpdate:(target:Vec3, ratio:number)=>{   
    						this.rope_node = target;   
    					}
    				}
    			)
    			// to 动作完成后会调用该方法
    			.call( ()=>{
    				that.rope_node.getComponent(cc.Sprite).spriteFrame = that.rope_imgs[0];
    				// 判断是否捕捉成功
    				if (that.success == true) {
    				    let  scoreLabel = cc.find("Canvas/spt_bg/lb_score").getComponent(cc.Label);
    				    scoreLabel.string = "Score:" + that.scoreNum;
    				    that.success = false;
    				}
    			})
    		cc.tween(tween_node).sequence(t1, t2).start(); // 将 t1 和 t2 两个缓动加入到新的缓动队列内
    		
    		
    	}
    	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    11.2.3 游戏结束,展示结果弹窗

    start() {
    		
    		// 获取 AudioSource 组件
    		const audioSource_set = this.node.getComponent(AudioSource)!;
    		// 检查是否含有 AudioSource,如果没有,则输出错误消息
    		assert(audioSource_set);
    		// 将组件赋到全局变量 audioSource 中
    		this.audioSource = audioSource_set;
    		
    		let btn_capture_node = cc.find("Canvas/spt_bg/btn_capture");				
    		btn_capture_node.on(NodeEventType.TOUCH_START, this._touchStart_btn_capture, this);
    		
    		
    		this.success = false;
    		// 初始分数
    		this.scoreNum = 0;
    		
    		
    		let countDownLabel = cc.find("Canvas/spt_bg/lb_count_down").getComponent(cc.Label);
    		countDownLabel.string = this.time + "s";
    		
    		
    		this.schedule(function () {
    		    this.time --;
    		    countDownLabel.string = this.time + "s";
    		    if (this.time == 0) {
    		        cc.log("游戏结束!");
    		        // 获取弹窗节点
    		        let resultNode = cc.find("Canvas/spt_result");
    				
    				//绑定关闭事件
    				let closeNode = cc.find("Canvas/spt_result/close");
    				closeNode.on(NodeEventType.TOUCH_START, this._touchStart_close, this);
    				
    		        // 获取title和content两个节点
    		        let titleNode = resultNode.getChildByName("lb_title");
    		        let contentNode = resultNode.getChildByName("lb_content");
    		        // 展示分数
    		        titleNode.getComponent(cc.Label).string = "最终得分 " + this.scoreNum;
    		        // 获取组件
    		        let contentLabel = contentNode.getComponent(cc.Label);
    		        switch (true) {
    		            case this.scoreNum <= 3:
    		                contentLabel.string = "套牛新手";
    		                break;
    		            case this.scoreNum < 6:
    		                contentLabel.string = "套牛神手";
    		                break;
    		            case this.scoreNum >= 6:
    		                contentLabel.string = "套牛圣手";
    		                break;
    		
    		        }
    		        resultNode.active = true;
    		        let score = this.scoreNum;
    		       
    		
    		        cc.director.pause();
    		
    		    }
    		
    		},1);
    		
    		
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    11.2.4 关闭按钮

    // 关闭按钮,继续游戏
    	_touchStart_close(touch: Touch, event: EventTouch) {
    	    cc.log("继续游戏");
    	    cc.director.resume();
    	    cc.director.loadScene(this._mainScene);
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    错误:因为函数后有逗号
    index.js:1 Error: Unable to resolve bare specifier ‘__unresolved_2’ from http://localhost:7456/scripting/x/chunks/34/341ab3341f38daa5d6eb502f45f16e9d150576f5.js (SystemJS Error#8 https://git.io/JvFET#8)

    参考

    粒子和音频

    补充说明

    资源来源于网络各种免费分享,及自身学习总结,
    若有不当可评论指正

    A
    B
    C
    D
  • 相关阅读:
    [NPUCTF2020]ezinclude
    Java并发编程:start和run的区别
    C++ STL(九) -------- 哈希表封装unordered_map和unordered_set
    20.springboot项目打包成war文件,在独立的Tomcat上运行
    windows11恢复ie浏览器的方法教程
    jenkins 中文乱码及执行报错
    Flask框架——MongoEngine使用MongoDB数据库
    使用dumuz工具实现淘宝收藏的宝贝批量下载(批量导出)
    数字化营销系统如何为消费者提供精细化服务?又有哪些特点?
    2024年最新版小程序云开发数据模型的开通步骤,开始开发微信小程序前的准备工作,认真看完奥!
  • 原文地址:https://blog.csdn.net/Hello_World_CFF/article/details/126354414