手机扫码体验:
下载❝Cocos官网:https://www.cocos.com
❞
Cocos
是一款游戏开发引擎,提供了一套完整的游戏开发解决方案,包括渲染引擎、物理引擎、UI编辑器、动画编辑器等,支持多种开发语言并且可以发布到不同的平台。
刚开始接触,看着互联网上各种版本的资料,一头雾水,好几个版本究竟该学哪个用哪个,都有啥区别。
随着Cocos
的发展,出现了不同的版本,Cocos2d-x
,Cocos2d-js
,Cocos Creator
。
Cocos2d-js
是Cocos2d-x
的子集,Cocos2d-x
支持 C++、Javascript 及 Lua 三种语言来进行游戏开发,并且支持所有常见平台,包括 iOS、Android、Windows、macOS、Linux。
Cocos Creator 3.x
底层使用全新3D内核重写,是当前Cocos Creator
最新版本,与2.x版本的api不完全兼容,存在很多差异,这也导致查看网络上很多教程的时候使用2.x在3.x中无法调用。
Cocos Creator 2.x
底层基于Cocos2d-x
的精简版本,已经停止更新。
特性 | Cocos2d-x | Cocos Creator |
---|---|---|
编程语言 | C++, Lua, JavaScript | JavaScript, TypeScript |
平台支持 | iOS, Android, Windows, macOS, Linux, HTML5 | iOS, Android, Windows, macOS, Linux, HTML5 |
渲染引擎 | Cocos2d-x 自带渲染引擎 | Cocos2d-x-lite, Cocos3D |
UI编辑器 | Cocos Studio (已停止更新) | Cocos Creator 内置 UI 编辑器 |
动画编辑器 | Cocos Studio (已停止更新) | Cocos Creator 内置动画编辑器 |
状态 | 发布于 2010 年,2019 年停止更新 | 发布于 2021 年初,3.x是当前 Cocos Creator 的最新版本 |
总结下来,作为初学者,希望快速构建游戏,学习Cocos Creator3.x
就行了。
❝官网软件下载并安装:https://www.cocos.com/creator-download api文档:https://docs.cocos.com/creator/api/zh/
❞
一个游戏会分为多个场景,场景可以被理解为游戏的一个"舞台"或者"房间"。它是在游戏中创建、编辑和组织游戏对象(如角色、背景、道具等)的地方。
作为一个前端开发,这不就是页面吗,往页面(场景)里添加组件(角色、背景、道具)内容,然后在不同的页面(场景)中跳转路由(切换场景)。
导演director
实例化单例负责控制游戏的整体流程和状态,可以把它想象成一个电影导演,负责指挥游戏中的各个部分,确保游戏按照预期的方式运行,例如切换场景,暂停游戏,获取场景等。
举个例子加载场景:director.loadScene('index')
,加载index
场景。
节点是场景中的基本单位,它代表一个游戏对象,如角色、道具等。节点可以包含其他节点,形成一个树形结构。节点具有位置、缩放、旋转等属性,可以通过改变这些属性来控制游戏对象的表现。
DOM元素
预制件是预先设计好的游戏对象模板,可以在场景中快速创建和复制。预制件包含了节点、组件和资源的配置信息,方便在游戏中重复使用。
有点像前端中的组件模版,把写好的内容抽离成组件,下次直接使用这个已经实现的组件,而不是再次cv复制一次该节点。
组件是附加在节点上的脚本或功能模块,用于定义节点的行为。例如,一个角色节点可以附加一个移动组件来控制角色的移动。组件之间可以相互通信,实现复杂的游戏逻辑。
在cocos中,ts脚本也是一个Component
,例如新建一个ts控制器,添加给这个角色后就可以通过这个ts脚本去控制这个角色。
资源是游戏中使用的各种素材,如图片、音频、字体等。资源可以在编辑器中导入、管理和分配给节点、组件等。
❝官方文档:https://docs.cocos.com/creator/manual/zh/editor/
❞
官方文档介绍比较详细了,简单了解一下
image-20230921175030086❝静态资源下载
链接: https://pan.baidu.com/s/16q28nFUspnYOOdWqVNYMiA?pwd=32jc 提取码: 32jc
❞
使用安装好的Cocos Dashboard
新建一个2D空项目
❝相关文档地址:https://docs.cocos.com/creator/manual/zh/asset/dynamic-load-resources.html
❞
使用动态加载资源,动态加载资源指的是在游戏运行过程中按需加载和卸载资源,而不是在游戏开始时一次性加载所有资源。这种方式可以有效地减少游戏的启动时间和内存占用,提高游戏性能。
值得注意的是动态加载的资源「必须」要放在assets/resources
文件夹中,默认是没有该文件夹,需要手动创建,该文件夹中的资源可以通过resources
相关的api进行操作。
将需要的资源移动到resources
中
背景图片大小是480*852
,新建一个480*800
的画布。
windows调整路径:顶部菜单栏 Project->Project Settings
,Mac电脑直接点击如图框选位置。
这里避免黑边问题,可以将Fit Width
取消勾选,并勾选Fit Hight
,以高度优先。
期望实现背景从上往下不断移动,看上去像是飞机再往前移动,要实现图片滚动,可以使用两张相同的图片,同时向下移动,当底部图片滚动出屏幕时,设置位置到顶部,依次循环即可实现。
我们可以在Canvas
下新建一个Node
节点并拖入两张背景图片,并设置好两张图片坐标刚好在上下位置,为了方便管理,可以对其进行重命名。
实现滚动脚本
当前的背景是静态不可动的,为了实现背景滚动,需要给Node
节点bg
添加一个TS脚本,并用脚本控制该节点下的所有背景图片向下移动和回到顶部。
(1)新建一个TS脚本assets/script/BgControl
(2)在bg
节点中添加Component
,可以点击bg
在右侧「属性检查器」中点击Add Component
选择BgControl
,也可以直接将BgControl
拖入到bg
右侧「属性检查器」中。
(3)编写BgControl
文件,通过读取bg
节点的子节点,在每帧调用生命周期中循环执行子节点也就是背景图片向下移动。
打开BgControl
文件可以看到存在两个生命周期方法start
和update
,start
在初始化的时候执行一次,update
会在每一帧执行,用于更新对象的状态,背景移动就需要在update
中编写。
export class BgControl extends Component {
start() {
}
update(deltaTime: number) {
}
}
❝生命周期回调介绍:https://docs.cocos.com/creator/manual/zh/scripting/life-cycle-callbacks.html
Node相关api文档:https://docs.cocos.com/creator/3.8/api/zh/class/Node
❞
通过循环获取当前节点下的子节点,并且拿到坐标值,将y轴坐标减去固定值,当这个值已经到底了,重新设置到顶部,新的轮回开始。
相关的api,例如获取子节点,获取坐标信息,这些需要在api文档上查询
「完整代码:」
import { _decorator, Component, director, Node } from "cc";
const { ccclass, property } = _decorator;
@ccclass("BgControl")
export class BgControl extends Component {
start() {}
// 生命周期每帧调用函数
update(deltaTime: number) {
// 使用this.node.children获取当前节点下的子节点
for (let item of this.node.children) {
// 使用getPosition获取坐标信息
const { x, y } = item.getPosition();
// 计算移动坐标
const moveY = y - 100 * deltaTime;
item.setPosition(x, moveY);
// 如果超出屏幕 重新回到顶部,也就是当前位置加上两倍的高度
if (moveY < -870) {
item.setPosition(x, moveY + 852 * 2);
}
}
}
}
update
方法中的deltaTime
参数是一个用于表示游戏中两帧之间的时间差的概念。在游戏开发中,游戏画面通常是由一帧帧图像连续播放组成的,每一帧都包含了游戏中所有元素的状态和位置。为了使游戏运行流畅,通常会尽量让每一帧的播放时间保持一致。
但是,由于计算机性能和系统负载等原因,,实际上每一帧的播放时间可能会有所不同。为了解决这个问题,引入了 deltaTime
这个概念。deltaTime
是一个表示两帧之间经过的时间(以秒为单位)的值。通过使用 deltaTime
,可以使游戏中的元素运动更加平滑,因为它们的速度会根据实际的时间差进行调整。
例如,如果一个游戏角色需要在 1 秒内移动 100 像素,那么在每一帧中,它的移动距离应该是 100 * deltaTime
。这样,即使每一帧的时间不同,角色的移动速度也会根据实际的时间差进行调整,使得在 1 秒内总共移动 100 像素。
如果直接调整 100 这个数值,那么元素的移动速度将会与帧率直接关联。在帧率较高的设备上,元素会移动得更快;而在帧率较低的设备上,元素会移动得更慢。这会导致游戏在不同设备上的表现和玩家体验不一致。
「运行查看效果」
p3ufm-5s27j飞机需要实现能够跟随鼠标或者手势按压的位置,并且能够发射子弹。
拖入飞机图片,并创建一个飞机的控制器/assets/script/PlayerControl
,将PlayerControl
拖入到飞机右侧「属性检查器」中。
2.实现跟随鼠标功能
「监听节点鼠标移动方法:」
❝节点事件api:https://docs.cocos.com/creator/manual/zh/engine/event/event-node.html
❞
Node.on(type: Node.EventType, callback: (e:EventTouch)=>void, target?: unknown, useCapture?: any):void
实现跟随鼠标移动,只需要监听Node.EventType.MOUSE_MOVE
类型,并在回调中设置飞机到鼠标位置即可。
this.node.on(Node.EventType.TOUCH_MOVE, (e: EventTouch) => {
console.log("监听到了",e);
});
callback
回调函数回传了一个EventTouch类型参数,可以通过这个参数中的方法获取鼠标移动的位置。
「获取触摸位置」
❝触摸事件api: https://docs.cocos.com/creator/manual/zh/engine/event/event-api.html
❞
获取触摸位置有两个方法,一个是全局触摸api:getLocation
,一个是节点触摸api:getUILocation
使用的时候发现getLocation
获取的坐标比getUILocation
大一倍,于是看了看官网的介绍,好像并没有说清楚两个具体区别
函数名 | 返回值类型 | 单位 | 意义 |
---|---|---|---|
「getLocation」 | Vec2 | 设备物理像素 | 获取鼠标位置对象,对象包含 x 和 y 属性。 |
「getUILocation」 | Vec2 | 设计分辨率 | 获取当前鼠标在 UI 窗口内相对于左下角的坐标位置,对象包含 x 和 y 属性。 |
这里两个区别涉及到「设计分辨率」和「设备物理像素」两个概念
「设计分辨率」是设计时设置的分辨率大小,「设备物理像素」是显示设备中的最小显示单元,物理像素的数量决定了显示设备的分辨率,分辨率越高,物理像素越多,图像越清晰。
getLocation()
获取坐标时,返回的坐标是基于「设备物理像素」的。如果设备像素比大于 1,那么返回的坐标可能会比设计分辨率的坐标大。
通过window.devicePixelRatio
可以获取「设备像素比」,比如我这里获取window.devicePixelRatio
结果为2,所以获取的坐标大了一倍,为了解决这个问题,可以将 getLocation()
返回的坐标除以设备像素比,可以获取与设计分辨率一致的坐标。
设置节点有两个类似的方法
setWorldPosition
:此方法用于设置节点在世界坐标系中的位置。世界坐标系是一个统一的坐标系,它不随任何父节点的改变而改变。
setPosition
:此方法用于设置节点在本地坐标系(相对于父节点)中的位置。本地坐标系是相对于父节点的坐标系,它会随父节点的改变而改变。
最后这里使用getUILocation
获取当前鼠标坐标,并且使用setWorldPosition
设置给当前节点。
import { _decorator, Component, EventTouch, Node, v3 } from "cc";
const { ccclass, property } = _decorator;
@ccclass("PlayerControl")
export class PlayerControl extends Component {
start() {
this.node.on(Node.EventType.TOUCH_MOVE, (e: EventTouch) => {
const { x, y } = e.getUILocation();
this.node.setWorldPosition(v3(x, y));
});
}
update(deltaTime: number) {}
}
「运行查看效果」
9rb9j-ej7fh拖入子弹到Canvas
节点下,并给子弹添加上新建的组件/assets/script/BulletControl
。
编写子弹内容,设置子弹位置不断往前,很简单的代码,获取坐标,并且y坐标增加。
update(deltaTime: number) {
const { x, y } = this.node.getPosition();
this.node.setPosition(x, y + 600 * deltaTime);
}
「预览效果」
7epbv-54mwo添加「预制件(Prefab)」
在前面基础概念有提到,子弹里包含ts脚本这些内容,为了创建更加方便,将子弹添加为「预制件」传递给飞机,飞机拿到子弹对象后,创建子弹即可实现发射子弹的效果。
(1)在「层级管理器」中将子弹拖入进/assets/prefab
文件夹中
(2)给飞机添加参数接收子弹对象
import { _decorator, Component, EventTouch, Node, Prefab, v3 } from "cc";
const { ccclass, property } = _decorator;
@ccclass("PlayerControl")
export class PlayerControl extends Component {
@property(Prefab)
bullet: Prefab = null;
start() {
this.node.on(Node.EventType.TOUCH_MOVE, (e: EventTouch) => {
const { x, y } = e.getUILocation();
this.node.setWorldPosition(v3(x, y));
});
}
update(deltaTime: number) {}
}
添加完成后在编辑器中就可以查看到这个属性,将子弹的预制件拖过去后,就可以在飞机类中操作子弹
image-20230923103351578(3)使用schedule定时器创建子弹
在cocos中存在schedule
定时器的方法,而js也提供有setInterval
方法能够实现定时执行,在场景切换、暂停、组件销毁时schedule
会自动停止运行,而setInterval
不会自动停止,可能会导致内存泄漏或其他问题,所以最好是使用cocos内置的定时器方法。
添加子弹对象有很多种方式,举两个方案
「方案1:」
首先通过api getPosition
拿到飞机的位置,通过导演类的方法获取场景下的Canvas
,并通过instantiate将子弹实例化成节点之后,添加到Canvas
画布中。
start() {
// ...
this.schedule(() => {
const { x, y } = this.node.getPosition();
// 通过名称获取节点的子节点。
const Canvas = director.getScene().getChildByName("Canvas");
const node = instantiate(this.bullet);
node.setPosition(x, y + 70);
Canvas.addChild(node);
}, 0.2);
}
「方案2:」
直接通过节点下的方法setParent
设置子弹的父节点为当前飞机的父节点,也就是和飞机同级。
start() {
// ...
this.schedule(() => {
const { x, y } = this.node.getPosition();
const node = instantiate(this.bullet);
node.setParent(this.node.parent);
node.setPosition(x, y + 70);
}, 0.2);
}
用方案2还怪方便的嘿
(4)销毁子弹
在定时器中会不断地创建子弹,超出屏幕之后依然存在,这里需要简单的优化一下,子弹超出屏幕范围就进行销毁。
修改BulletControl
import { _decorator, Component, director, Node, view } from "cc";
const { ccclass, property } = _decorator;
@ccclass("BulletControl")
export class BulletControl extends Component {
start() {}
update(deltaTime: number) {
const { x, y } = this.node.getPosition();
const moveY = y + 600 * deltaTime;
this.node.setPosition(x, moveY);
// 判断超出屏幕销毁子弹
if(moveY > 800 ) {
this.node.destroy();
}
}
}
添加敌机与添加子弹类似,敌机从屏幕外由一个节点进行管理,完成一个敌机并添加脚本后,添加为预制件,并通过敌机管理节点随机x坐标进行创建。
(1)摆好位置
创建一个enemy
空节点,并把airplane
敌机拖入空节点中,移动空节点到顶部屏幕外
(2)创建一个/assets/script/EnemyControl
敌机脚本并添加给airplane
,修改代码设置敌机不断前进,超出屏幕后进行销毁,与子弹代码类似,只是方向不同
import { _decorator, Component, Node } from "cc";
const { ccclass, property } = _decorator;
@ccclass("EnemyControl")
export class EnemyControl extends Component {
start() {}
update(deltaTime: number) {
const { x, y } = this.node.getPosition();
const moveY = y - 600 * deltaTime;
this.node.setPosition(x, moveY);
if (moveY < -900) {
this.node.destroy();
}
}
}
运行查看效果
动画(3)设置敌机为预制件,拖入到/assets/prefab
中
(4)添加一个/assets/script/EnemyManager
敌机管理器脚本,并添加接收预制件参数,把敌机拖入其中
(5)用定时器随机位置不断创建敌机
与创建子弹一致,实例化预制件,可以在x轴上拖动敌机查看大概位置并设置敌机的移动范围,使用随机函数Math.random() * 400 + 40
获取40-440的随机值赋值给x轴,并直接在敌机管理器中添加敌机。
import { _decorator, Component, instantiate, Node, Prefab } from "cc";
const { ccclass, property } = _decorator;
@ccclass("EnemyManager")
export class EnemyManager extends Component {
@property(Prefab)
enemy: Prefab;
start() {
const { x, y } = this.node.getPosition();
this.schedule(() => {
const node = instantiate(this.enemy);
node.setPosition(Math.random() * 400 + 40, y);
this.node.addChild(node);
}, 0.5);
}
update(deltaTime: number) {}
}
查看效果
❝2D刚体组件文档:https://docs.cocos.com/creator/manual/zh/physics-2d/physics-2d-rigid-body.html
❞
首先需要了解一下cocos中的物理引擎相关内容,为了让子弹和敌机碰撞,需要给他们添加物理引擎中的「2D刚体组件」,「2D刚体」可以让游戏中的对象具有真实世界中的物体特性。例如,当你在游戏中扔一个球,2D 刚体可以让这个球在空中受到重力的影响,下落时与地面发生碰撞,并在碰撞后产生弹跳效果。这些都是基于物理学原理的模拟,让游戏中的对象表现得更加真实。
在Cocos Creator 3.x
中默认的物理引擎基于Box2d的2d物理系统
,只有添加了刚体组件的物体才能进行碰撞检测。这是因为物理引擎是通过刚体之间的碰撞器组件进行碰撞检测的。
刚体组件(RigidBody)
和碰撞器组件(Collider)
共同决定了物体的碰撞行为。刚体组件为物体提供物理属性,如质量、速度等,而碰撞器组件则定义了物体的形状和碰撞区域。当两个带有刚体组件和碰撞器组件的物体接触时,物理引擎会检测到碰撞并触发相应的碰撞事件。
(1)了解刚体组件(RigidBody2D)
组件相关配置
在2D刚体中,有很多配置项,官网文档中讲的很详细,这里主要列举一下需要修改到的下面两个参数
属性 | 说明 |
---|---|
「EnabledContactListener」 | 开启监听碰撞回调 |
「Type」 | 刚体类型,详情请参考下方 「刚体类型」 |
刚体类型 | 说明 |
---|---|
「Static」 | 静态刚体,零质量,零速度,即不会受到重力或速度影响,但是可以设置他的位置来进行移动。该类型通常用于制作场景 |
「Dynamic」 | 动态刚体,有质量,可以设置速度,会受到重力影响。唯一可以通过 applyForce 和 applyTorque 等方法改版受力的刚体类型 |
「Kinematic」 | 运动刚体,零质量,可以设置速度,不会受到重力的影响,但是可以设置速度来进行移动 |
「Animated」 | 动画刚体,在上面已经提到过,从 Kinematic 衍生的类型,主要用于刚体与动画编辑结合使用 |
(2)给敌机和子弹添加刚体组件(RigidBody2D)
和盒碰撞组件(BoxCollider2D)
分别给双击敌机和子弹的预制件(Prefab)
,在右侧属性检查器中
点击Add Component
添加刚体组件(RigidBody2D)
和盒碰撞组件(BoxCollider2D)
(3)配置刚体组件勾选EnabledContactListener
和设置Type为Kinematic
,并把敌机的BoxCollider2D
中Tag
的值修改为1,后续需要通过Tag
进行判断是否是敌机,做一个标识。
(4)添加碰撞体回调函数
❝碰撞体回调文档:https://docs.cocos.com/creator/manual/zh/physics-2d/physics-2d-contact-callback.html
❞
有两种实现方案可以触发碰撞体回调,「指定 collider 注册」和「通过 2D 物理系统注册全局回调函数」
「指定 collider 注册」这种方式是将回调函数直接绑定到特定的 collider 组件上。这意味着,只有与该 collider 发生碰撞的事件才会触发这个回调函数。这样可以更精确地控制哪些对象需要处理碰撞事件,从而提高代码的可读性和可维护性。
这种方法的缺点是,如果您有多个对象需要处理碰撞事件,需要为每个对象分别注册回调函数。这可能会导致代码重复和更难管理。
「通过 2D 物理系统注册全局回调函数」这种方式是将回调函数注册到整个 2D 物理系统。这意味着,无论哪两个碰撞体发生碰撞,都会触发这个回调函数。这可以让您集中处理所有的碰撞事件,减少代码重复。
这种方法的缺点是,需要在回调函数中检查发生碰撞的对象,并根据需要执行相应的操作。这可能会导致回调函数变得庞大和复杂,从而降低代码的可读性和可维护性。
两种方法各有优缺点,可以根据项目的需求和个人编程风格来选择合适的方法
这次我选择使用全局回调方式在BgControl
背景脚本上注册回调
通过 2D 物理系统在注册一个全局的回调函数,执行验证一下是否能够触发碰撞回调
start() {
PhysicsSystem2D.instance.on(
Contact2DType.BEGIN_CONTACT,
this.onBeginContact,
this
);
}
// 只在两个碰撞体开始接触时被调用一次
onBeginContact(self: Collider2D, other: Collider2D) {
console.log("self",self,"other",other)
}
image-20230925145240345
当子弹碰撞到敌机后,敌机需要进行销毁,在敌机EnemyControl
和子弹BulletControl
脚本中添加销毁函数。
调用die
方法后,执行销毁方法,并且通过isDead
参数过滤掉多次的执行和死亡后的移动。
在代码中延迟销毁主要原因是因为
如果在销毁对象之前还有其他操作需要处理,立即销毁对象可能会导致这些操作无法正常执行。
如果销毁对象时,其他对象还在处理与该对象相关的操作,立即销毁可能会导致错误或异常。
后面敌机需要添加死亡动画
import { _decorator, Component, Node } from "cc";
const { ccclass, property } = _decorator;
@ccclass("EnemyControl")
export class EnemyControl extends Component {
isDead: boolean = false;
start() {}
update(deltaTime: number) {
if (this.isDead) return;
const { x, y } = this.node.getPosition();
const moveY = y - 500 * deltaTime;
this.node.setPosition(x, moveY);
if (moveY < -900) {
this.node.destroy();
}
}
// 敌机死亡调用
die() {
if (this.isDead) return;
this.isDead = true;
setTimeout(() => {
this.node?.destroy?.();
}, 200);
}
}
子弹销毁和敌机销毁一致
import { _decorator, Component } from "cc";
const { ccclass, property } = _decorator;
@ccclass("BulletControl")
export class BulletControl extends Component {
isDead: boolean = false;
start() {}
update(deltaTime: number) {
if (this.isDead) return;
const { x, y } = this.node.getPosition();
const moveY = y + 500 * deltaTime;
this.node.setPosition(x, moveY);
if (moveY > 800) {
this.node.destroy();
}
}
die() {
if (this.isDead) return;
this.isDead = true;
setTimeout(() => {
this.node?.destroy?.();
}, 10);
}
}
碰撞回调出发后,判断子弹和敌机碰撞,执行销毁两个的对象。
修改BgControl
回调方法, 当tag等于1和0的时候,说明是子弹和敌机相撞,销毁对应的对象。
// 只在两个碰撞体开始接触时被调用一次
onBeginContact(self: Collider2D, other: Collider2D) {
if (other.tag === 1 && self.tag === 0) {
other.getComponent(EnemyControl).die();
self.getComponent(BulletControl).die();
} else if (other.tag === 0 && self.tag === 1) {
other.getComponent(BulletControl).die();
self.getComponent(EnemyControl).die();
}
}
查看效果
contact-over❝加载资源文档:https://docs.cocos.com/creator/manual/zh/asset/dynamic-load-resources.html
❞
简单实现就是通过加载多张图片,循环替换图片即可。
将敌机死亡的几张图片放入assets/resources/enemy-death
在Cocos Creator 2.4
以前是通过 loader
模块加载,网上一些旧的资料都是使用的loader
模块,与现在的新版本不兼容,在之后的版本使用assetManager
下的resources
进行操作。
编写敌机脚本EnemyControl
添加加载图片方法,使用resources.loadDir
加载整个文件夹资源,并且把加载的资源使用变量保存下来。
// 加载图片
loadImages() {
resources.loadDir(
"enemy-death",
SpriteFrame,
(_err, spriteFrames) => {
this.airplaneDeadImages = spriteFrames;
}
);
}
添加播放动画,循环播放加载的资源,使用延时方式执行
// 播放死亡动画
playDead() {
for (let i = 0; i < this.airplaneDeadImages.length; i++) {
setTimeout(() => {
if (this.node.getComponent) {
this.node.getComponent(Sprite).spriteFrame =
this.airplaneDeadImages[i];
}
}, i * 80);
}
}
最后进行调用加载图片和播放方法,完整代码
import { _decorator, resources, Component, Sprite, SpriteFrame } from "cc";
const { ccclass, property } = _decorator;
@ccclass("EnemyControl")
export class EnemyControl extends Component {
isDead: boolean = false;
airplaneDeadImages = [];
start() {
this.loadImages();
}
update(deltaTime: number) {
if (this.isDead) return;
const { x, y } = this.node.getPosition();
const moveY = y - 500 * deltaTime;
this.node.setPosition(x, moveY);
if (moveY < -900) {
this.node.destroy();
}
}
// 加载图片
loadImages() {
resources.loadDir(
"enemy-death",
SpriteFrame,
(_err, spriteFrames) => {
this.airplaneDeadImages = spriteFrames;
}
);
}
// 播放死亡动画
playDead() {
for (let i = 0; i < this.airplaneDeadImages.length; i++) {
setTimeout(() => {
if (this.node.getComponent) {
this.node.getComponent(Sprite).spriteFrame =
this.airplaneDeadImages[i];
}
}, i * 80);
}
}
// 敌机死亡调用
die() {
if (this.isDead) return;
this.isDead = true;
this.playDead();
setTimeout(() => {
this.node?.destroy?.();
}, 300);
}
}
打包构建就比较简单了,右上角点击 Build
,选择Web Mobile
后点击Build
就开始构建啦。
完整代码:https://github.com/yiqia/cocos-plane
通过这次实践,了解了 Cocos Creator 3.x 的基本概念和使用方法,并成功实现了一个简单的飞机大作战游戏。在这个过程中,实现创建场景、节点、预制件以及编写脚本来控制游戏对象的行为。同时,使用物理引擎中的刚体组件和碰撞器组件来实现碰撞检测,以及如何加载资源和简单版播放动画。
虽然这个游戏还有很多可以优化和完善的地方,还可以继续完善游戏,例如添加新的场景,添加开始游戏和结束游戏,以及生命值,关卡等扩展,但通过这个实践,已经掌握了 Cocos Creator 3.x 的基本使用方法,为以后开发更复杂的游戏奠定了基础,可以尝试更多关于 Cocos Creator 的知识,如动画系统、粒子系统、UI 系统等,以便能够制作更加丰富和有趣的游戏。
如果文章对你有帮助的话欢迎
「关注+点赞+收藏」