微信飞机大战小游戏制作分享(上集)
  HTXrUCxIDXqo 2023年11月02日 47 0


参考:​​制作视频地址​

1. 下载游戏引擎

阿菌用的是 ​​cocos creator​​​,官网地址:​​链接​

您可以直接点击下载免费获得这款引擎软件,建议下载最新的稳定版本哦,写这篇文章的时候,适合写 2D 小游戏的最新版本貌似是:​​V2.4.8​

一般我们会先下载一个 DASHBOARD,可以理解成一个 cocos 的盒子,用于放各种游戏工具。

然后从 DASHBOARD 中下载相应版本的 cocos creator。

2. 创建游戏项目

安装完毕后,我们进入大盘界面:

微信飞机大战小游戏制作分享(上集)_微信飞机大战

点击 new 创建一个新的 ​​empty​​ 游戏工程:

微信飞机大战小游戏制作分享(上集)_小游戏_02

看到上面这个界面后,就意味着咱们的开发环境已经搭建好了,可以开始开发小游戏咯!

3. 创建游戏画布

首先我们把游戏资源导入到我们的游戏工程中,游戏资源地址:

然后查看背景图片的大小,资源里的背景图大小是:​​640 x 1136​

微信飞机大战小游戏制作分享(上集)_微信飞机大战_03

于是我们设置画布的大小为 ​​640 x 1136​

微信飞机大战小游戏制作分享(上集)_小游戏_04

4. 设置游戏背景

为了减少加载图片所带来的性能损耗,我们把所有的资源合到一张图片中(cocos 能够自动识别这些图片),合图工具用的是:TexturePacker,​​官方下载地址链接​

简单的使用方法如下:

  1. 打开图片目录,把所有的图片拖拽到 TexurePacker 中

微信飞机大战小游戏制作分享(上集)_微信飞机大战_05

  1. 保存

微信飞机大战小游戏制作分享(上集)_微信飞机大战_06

制作完 plist 文件后,我们新建一个叫 plist 的文件夹存放 plist 文件,后续我们使用图片都通过 plist 获取。

微信飞机大战小游戏制作分享(上集)_微信飞机大战_07

然后我们把背景图片 bg 拖拽到节点树上:

微信飞机大战小游戏制作分享(上集)_小游戏_08

为了制作一个轮播的背景,我们需要两张背景图,假设为 A 和 B(想让背景图片动起来,我们必须保证画布中永远有背景)。详细解释参见:​​链接​

首先我们创建一个控制游戏逻辑的 TypeScript 脚本:

微信飞机大战小游戏制作分享(上集)_微信飞机大战_09

然后把脚本文件关联到画布中:

微信飞机大战小游戏制作分享(上集)_小游戏_10

关联到画布后,咱们编写脚本,控制背景图片轮播,首先需要定义两个节点(我们只使用背景图片的坐标属性,因此定义为节点类型就足够了):

@property(cc.Node)
bg1: cc.Node = null;

@property(cc.Node)
bg2: cc.Node = null;

定义完成后记得到画布中进行绑定:

微信飞机大战小游戏制作分享(上集)_小游戏_11

绑定后我们编写轮播的代码:

// 在游戏加载的时候定义背景图片的位置
protected onLoad() {
this.bg1.y = 0
this.bg2.y = this.bg1.y + this.bg2.height
}

update (dt) {
// 背景图片的移动速度
this.bg1.y -= 10;
this.bg2.y -= 10;
// 背景图片轮播逻辑(没明白可以看视频哦)
if(this.bg1.y <= -this.bg1.height){
// 当一张背景移动到屏幕外面后,立马补到另一张背景图片的后面
this.bg1.y = this.bg2.y + this.bg1.height
}
if(this.bg2.y <= -this.bg2.height){
this.bg2.y = this.bg1.y + this.bg2.height
}
}

编写完成后,咱们就有了轮播的背景图。

微信飞机大战小游戏制作分享(上集)_小游戏_12

5. 添加开始游戏标语

首先我们把游戏标语添加到节点树中,选择一个合适的位置:

微信飞机大战小游戏制作分享(上集)_微信飞机大战_13

为了提示玩家点击屏幕开始游戏,我们在这个节点下添加一个 label 节点用于显示文字:

微信飞机大战小游戏制作分享(上集)_微信飞机大战_14

为了模仿微信飞机大战的展示效果,我们给这行 ​​点击屏幕开始游戏​​ 的文字添加一个上下晃动的动画效果,这里用到了 cocos 的 animation 功能:

  1. 首先我们创建一个动画资源
  2. 将动画资源绑定到​​点击屏幕开始游戏​​ 所在的节点上
  3. 然后设置关键帧,给每个关键帧设定恰当的角度,比如说第一帧的角度为 0,第二帧的角度为 15,第三帧的角度为 0,第四帧为 -15,第五帧为 0。

微信飞机大战小游戏制作分享(上集)_微信飞机大战_15

连起来播放后大概就成了这样:

微信飞机大战小游戏制作分享(上集)_小游戏_16

6. 编写游戏准备、游戏中、游戏暂停三个状态

虽然我们编写好了一个轮播的背景,有了动态的效果,但我们希望在游戏刚开始的时候背景是不动的,等玩家进入游戏后背景再动起来,让小飞机有飞翔的效果。

首先我们定义一个判断背景图片是否在动的变量

isBgMove = false

然后把移动背景的代码封装到一个方法里,在 update 方法中通过判断 isBgMove 变量控制背景是否移动:

update (dt) {
if(this.isBgMove){
this.moveBg()
}
}

moveBg(){
// 让背景图片动起来
this.bg1.y -= 10;
this.bg2.y -= 10;
if(this.bg1.y <= -this.bg1.height){
this.bg1.y = this.bg2.y + this.bg1.height
}
if(this.bg2.y <= -this.bg2.height){
this.bg2.y = this.bg1.y + this.bg2.height
}
}

有了控制背景轮播的开关后,我们编写游戏准备、游戏中、游戏暂停三个状态。

首先我们把上面定义的 shoot_copyright 节点改个名字,改成游戏准备节点 status_ready;

然后依次创建两个空节点 status_playing 和 status_pause:

微信飞机大战小游戏制作分享(上集)_小游戏_17

然后在游戏开始页面创建一个暂停按钮:

微信飞机大战小游戏制作分享(上集)_小游戏_18

为暂停按钮创建一个点击事件,我们编写一个通用处理点击事件的方法,通过控制台进行调试:

clickButton(sender, str){
if(str == "pause"){
console.log("点击了暂停按钮")
}
}

然后编写暂停页面:

  1. 首先添加一个暂停的背景图,设置透明度遮盖原有的游戏背景
  2. 添加三个按钮,设置好按钮的样式以及显示内容
  3. 为暂停背景设置一个 BlockInputEvents,屏蔽调下层节点

微信飞机大战小游戏制作分享(上集)_微信飞机大战_19

然后编写这三个页面的显隐关系,大概的逻辑是:

  1. 玩家进入游戏后显示一个静态的背景,当点击屏幕后隐藏调游戏准备界面,进入游戏开始界面
  2. 玩家可以在游戏界面点击暂停按钮,点击暂停按钮后由游戏开始界面进入游戏暂停界面
  3. 在游戏暂停界面有三个按钮,点击继续游戏会回到游戏开始界面,点击重新开始也会回到游戏开始界面,点击回到主页会回到游戏准备界面

以上的逻辑可以通过统一的按键点击方法进行处理:

clickButton(sender, str){
if(str == "pause"){
// 点击暂停后显示暂停页面
this.pause.active = true
}else if(str == "continue"){
// 点击继续游戏后隐藏暂停页面
this.pause.active = false
}else if(str == "restart"){
// 点击重新开始后隐藏暂停页面
this.pause.active = false
}else if(str == "backHome"){
// 点击回到主页隐藏暂停界面,停止游戏,停止背景移动
this.pause.active = false
this.playing.active = false
this.isBgMove = false
this.ready.active = true
}
}

实现效果大概是这样的:

微信飞机大战小游戏制作分享(上集)_微信飞机大战_20

7. 编写游戏主角我方飞机

编写好了游戏场景切换功能后,我们开始编写我们的游戏主角。

首先添加一个节点,为它设置一张图片(Sprite 属性),然后制作一个小动画:

微信飞机大战小游戏制作分享(上集)_微信飞机大战_21

然后编写飞机跟随手指(鼠标)移动的逻辑,简单来说就是要注册一个触摸移动的监听事件:

setTouch() {
// ....
this.node.on("touchmove", (event) => {
// 获取飞机的位置
let hero_pos = this.hero.getPosition()
// 获取手指(鼠标)距离上一次事件移动相对于左下角的距离对象
let move_pos = event.getDelta()
// 飞机的位置加上移动的相对位置得到飞机的最新位置
this.hero.setPosition(cc.v2(hero_pos.x + move_pos.x, hero_pos.y + move_pos.y))
}, this);
//...
}

大概的效果是酱紫的:

微信飞机大战小游戏制作分享(上集)_小游戏_22

8. 让飞机可以发射子弹

由于子弹是会重复利用的资源,我们这里采用预制体资源,首先我们在节点树中创建一个子弹节点,然后给子弹配一个脚本:

在每一帧中改变子弹的 y 值,让子弹有发射的效果。

update(dt) {
this.node.y += 10
}

配置好脚本后,我们把子弹节点拖拽到资源管理器中,使其变成一个预制体,然后编写主逻辑脚本,先定义一个预制体:

// 子弹
@property(cc.Prefab)
pre_bullet: cc.Prefab

然后尝试在每次鼠标点击结束(触摸手指离开屏幕)的时候生成一颗子弹:

setTouch() {
this.node.on("touchend", (event) => {
//......
// 生成一颗子弹
let bullet = cc.instantiate(this.pre_bullet)
// 把子弹挂在到节点树上
bullet.parent = this.node
// 获取飞机主角的位置
let pos = this.hero.getPosition()
// 设置子弹的初始位置为飞机头
bullet.setPosition(cc.v2(pos.x, pos.y + this.hero.height / 2))
}, this);
}

这样一来飞机就可以发射子弹了:

微信飞机大战小游戏制作分享(上集)_小游戏_23

9. 对象池与单例

现在虽然能不停地发射子弹了,但是一直创建子弹实例不进行删除可不行,如果游戏时间久了游戏会越来越卡。

我们使用 cocos 提供的对象池对子弹进行缓存,先编写一个生成子弹的方法:

createBullet() {
// 创建子弹的方法
let bullet = null
// 生成子弹的时候先到对象池中取
if (this.bulletPool.size() > 0) {
// 如果对象池中有子弹对象则直接使用
bullet = this.bulletPool.get()
} else {
// 如果对象池没有子弹了,就创建一颗新的子弹
bullet = cc.instantiate(this.pre_bullet)
}
// 获取子弹后挂在跟节点下
bullet.parent = this.node
// 获取飞机的位置
let pos = this.hero.getPosition()
// 设置子弹的初始位置
bullet.setPosition(cc.v2(pos.x, pos.y + this.hero.height / 2))
}

然后编写子弹消亡的逻辑,目前一共有三个场景可以回收子弹:

  1. 重新开始游戏
  2. 回到主页
  3. 子弹超出画布范围
// 回收单颗子弹
bulletKilled(bullet) {
// 回收子弹的方法
bullet.setPosition(cc.v2(0, 0))
this.bulletPool.put(bullet)
}

// 回收全部子弹
removeBullets() {
let children = this.node.children
for (let i = children.length - 1; i >= 0; i--) {
let bullet = children[i].getComponent("bullet")
if (bullet) {
this.bulletKilled(children[i])
}
}
}

阿菌在开发的时候比较困扰的问题是,我给子弹单独创建一个脚本后,怎么在子弹脚本中引用主逻辑类中的方法呢?

通过在 cocos 论坛搜索,大佬给出的答案是使用单例,单例的简单使用模版如下:

@ccclass
export default class Singleton extends cc.Component {

// 单例
public static instance: Singleton = null

onLoad() {
// 初始化单例
if (Singleton.instance == null) {
Singleton.instance = this
} else {
this.destroy()
return
}

通过上面的代码,把主逻辑对象导出,在子弹脚本中可以这么使用:

const {ccclass, property} = cc._decorator;
// 导入主逻辑类
import Singleton from "./main";

@ccclass
export default class NewClass extends cc.Component {

update(dt) {
this.node.y += 15
if(this.node.y > 590){
// 使用主逻辑单例对象
Singleton.instance.bulletKilled(this.node)
}
}
}

10. 添加敌机

添加敌机的逻辑和添加子弹的逻辑相似:

  1. 在节点树中创建一个敌机节点
  2. 创建敌机的脚本,并关联给敌机节点
  3. 把敌机节点制作成 PerFab
  4. 在主逻辑中编写敌机对象池、敌机创建、敌机销毁的方法
createEnemy1() {
// 创建敌机1的方法
let enemy1 = null
if (this.enemy1Pool.size() > 0) {
enemy1 = this.enemy1Pool.get()
} else {
enemy1 = cc.instantiate(this.pre_enemy_1)
}
enemy1.parent = this.node
enemy1.setPosition(cc.v2(0, 590))
}

enemy1Killed(enemy1){
this.enemy1Pool.put(enemy1)
}

removeEnemy1s() {
let children = this.node.children
for (let i = children.length - 1; i >= 0; i--) {
let enemy1 = children[i].getComponent("enemy1")
if (enemy1) {
this.bulletKilled(children[i])
}
}
}

在敌机脚本中设置敌机移动后,得到的效果大概是这样子的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jujgCh49-1647963420444)(https://s21.aconvert.com/convert/p3r68-cdx67/6d7lo-6fcx8.gif)]

11. 子弹与敌机碰撞

有了敌机之后,我们让我方飞机发射的子弹可以击中敌机。

首先我们给敌机和子弹添加碰撞组件(记得给子弹和敌机添加分组):

微信飞机大战小游戏制作分享(上集)_微信飞机大战_24

然后到项目设置中设置敌机和子弹可以碰撞:

微信飞机大战小游戏制作分享(上集)_小游戏_25

接下来编辑敌机死亡的帧动画(还要编辑一个敌机正常状态的动画):

微信飞机大战小游戏制作分享(上集)_微信飞机大战_26

然后在主逻辑中开启碰撞:

// 开启碰撞检测系统,未开启时无法检测
cc.director.getCollisionManager().enabled = true;

开启碰撞后,给子弹编写处理碰撞的方法:

onCollisionEnter(other, self) {
if (self.tag == 1) {
// 普通子弹命中了普通敌机
Singleton.instance.bulletKilled(this.node)
}
if (other.tag == 2){
// 击中的是普通敌机
let enemy = other.getComponent("enemy_1")
if(enemy && !enemy.isDie){
enemy.hit()
}
}
}

给敌机添加被击中后的处理逻辑:

hit(){
// 击中后状态设置为死亡
this.isDie = true
// 播放帧动画
let anim = this.getComponent(cc.Animation)
anim.play('enemy_1_die')
}

over(){
// 帧动画播放完后把敌机放回对象池中,等待下一次出现
Singleton.instance.enemy1Killed(this.node)
}

记得给敌机的出生坐标设置一个随机值

//...
enemy1.parent = this.node
let randomX = 295 - 590 * Math.random()
enemy1.setPosition(cc.v2(randomX, 590))
//...

得到的效果是这样的:

微信飞机大战小游戏制作分享(上集)_微信飞机大战_27

好了,上集就先到这,努力更新下集中......

参考:​​链接地址​

微信飞机大战小游戏制作分享(上集)_小游戏_28

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

HTXrUCxIDXqo