在上一篇文章中,我们已经将游戏的场景基本搭建完毕,接下来我们就可以为游戏编写代码并实现相关的核心逻辑了。
政安晨的个人主页:政安晨
欢迎 👍点赞✍评论⭐收藏
收录专栏: AI虚拟世界大讲堂
希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正!
使用计时器
在游戏开始后,敌人的招式图片会随机变化。
为了实现这一效果,我们可以每隔一段时间随机地显示三个招式的任意一个,并隐藏其余两个,从而实现招式不停变化的效果。
在Cocos Creator中,如果想要每隔一段时间触发一些行为,则可以使用计时器函数来实现。
我们可以通过计时器在固定的时间间隔重复执行某个行为,只需要在计时器的回调中将enemy_skill子节点进行随机显示与隐藏即可。
在资源管理器的scripts文件夹下创建Game脚本,然后将脚本挂载到Canvas节点上:
接着对Game脚本进行编写,代码如下:
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Game')
export class Game extends Component {
@property({ type: Node})
private enemySkillNode: Node = null; // 绑定 enemy_skill 节点
// 声明属性 children 的 cc 类型为 Node 数组
@property({ type: [Node] })
private children: Node[] = [];
private enemyAttackType = 0; // 敌人招式 0: 弓箭, 1: 流星锤, 2:盾牌
private timer = null; // 计时器
start() {
// 启动计时器, 每 0.1s 执行一次
this.timer = setInterval(() => {
this.randEnemyAttack();
}, 100);
}
// 敌人的随机招式
randEnemyAttack() {
this.enemyAttackType = Math.floor(Math.random() * 3); // 给敌人随机招式0~2
let allchildren = this.node.children; // 获取enemySkillNode下的所有子节点
allchildren.forEach(childNode => {
// 如果节点名字与随机招式的编号一致则显示, 否则将节点进行隐藏
if (childNode.name == this.enemyAttackType.toString())
{
childNode.active = true;
}
else
{
childNode.active = false;
}
});
}
update(deltaTime: number) {
}
}
脚本编写完成后,在属性检查器中将enemy_skill节点绑定到脚本上:
之后预览运行,此时敌人的招式图标已经可以随机变化了:
为了理解上面的代码,咱们这里再补充一下箭头函数的说明:
TypeScript是一种JavaScript的超集,在JavaScript的基础上添加了静态类型、类、模块等特性。箭头函数是TypeScript中的一种函数定义方式,它使用了箭头“=>”来表示函数的定义。
箭头函数的语法格式为:(参数列表) => 表达式或语句
其中,参数列表可以为空也可以包含一个或多个参数,多个参数之间使用逗号分隔。
表达式或语句可以是单个表达式或多个语句,如果是单个表达式,可以省略大括号{}和return关键字,直接返回表达式的结果。如果是多个语句,需要使用大括号{}将语句块包裹起来,并使用return关键字返回结果。
箭头函数的特点是可以继承当前上下文的this关键字,而不会创建一个新的this。这意味着在箭头函数中,this的值与包含它的函数相同。
以下是几个箭头函数的示例:
-
空参数的箭头函数: () => console.log("Hello, world!");
-
单个参数的箭头函数: (name) => console.log("Hello, " + name);
-
多个参数的箭头函数: (name, age) => console.log("Hello, " + name + ". You are " + age + " years old.");
-
箭头函数返回单个表达式的结果: (a, b) => a + b;
-
箭头函数返回多个语句的结果: (a, b) => { let sum = a + b; console.log(sum); return sum; }
箭头函数的使用使得函数的定义更加简洁和清晰,并且在处理this关键字的问题上更加方便。因此,在TypeScript中,箭头函数被广泛应用于编写可读性更高的代码。
好,现在咱们接着上面的例子:
在上面的代码中我们使用了计时器setInterval的方法,该方法会按照固定的周期(单位为毫秒)不停地调用randEnemyAttack函数,直到clearInterval被调用。同时,由setInterval返回的id值可以作为clearInterval的参数,因此我们事先定义了timer变量对其进行存储,以便后续进行相关处理。
使用Button组件
现在敌人已经可以进行随机出招了,接下来就需要为游戏添加相应的点击交互效果了。
在游戏中,我们希望在玩家点击三个技能中的任意一个后,由系统选定该招式为我方出招,同时使用选定的招式与敌人当前的招式进行比拼。
为了实现这一效果,我们需要在玩家点击任意一个招式时,触发事先编写的逻辑代码。在通常情况下,我们可以通过Button组件来实现这种交互效果。
同时,为了给Button添加相应的响应函数事件,我们还需要在Game脚本中添加如下代码:
(在Game脚本中增加如下代码,在类里添加,小伙伴们自己做一下)
@property({ type: Label})
private hintLabel: Label = null; // 绑定 hint 节点
// 出招按钮响应函数
attack (event, customEventData)
{
if (! this.timer)
{
return;
}
clearInterval(this.timer);
this.timer = null;
let pkRes = 0; //0: 平, 1:赢, -1:输
let attackType = event.target.name; // 获取目标节点的name
if (attackType == 0)
{
if (this.enemyAttackType == 0)
{
pkRes = 0;
}
else if (this.enemyAttackType == 1)
{
pkRes = 1;
}
else if (this.enemyAttackType == 2)
{
pkRes = -1;
}
}
else if (attackType == 1)
{
if (this.enemyAttackType == 0)
{
pkRes = -1;
}
else if (this.enemyAttackType == 1)
{
pkRes = 0;
}
else if (this.enemyAttackType == 2)
{
pkRes = 1;
}
}
else if (attackType == 2)
{
if (this.enemyAttackType == 0)
{
pkRes = 1;
}
else if (this.enemyAttackType == 1)
{
pkRes = -1;
}
else if (this.enemyAttackType == 2)
{
pkRes = 0;
}
}
if (pkRes == -1)
{
this.hintLabel.string = '失败';
}
else if (pkRes == 1)
{
this.hintLabel.string = '胜利';
}
else
{
this.hintLabel.string = '平局';
}
}
代码添加完成后,在属性检查器中将hint节点绑定到脚本上。
之后需要依次为技能图标添加Button组件。
此处以弓箭图标为例,在层级管理器中选中弓箭图标节点,选择【添加组件】→【UI】→【Button】命令,即可为节点添加Button组件,如下图所示。
Button组件的相关属性如下表所示。
为ClickEvents添加参数1后,可以在展开项中看到下表所示的属性:
接着将组件的【Transition】属性修改为【SCALE】,同时为【ClickEvents】绑定【Game】脚本中的【attack】函数,并将【CustomEventData】的值修改为代表弓箭图标的编号【0】,如下图所示。
添加“重新开始”功能
现在我们的游戏已经可以玩起来了,不过每次结束比拼后就不可以继续进行游戏了,因此需要为游戏的【重新开始】按钮实现对应的逻辑功能。可以直接使用loadScene函数对Game场景进行加载,实现“重新开局”的效果。
为Game脚本添加如下代码:
// 重新加载场景
restart()
{
director.loadScene('Game');
}
为场景右上角的【重新开始】按钮绑定restart函数,即可实现重新开始的功能了。这里需要注意的是,使用loadScene加载的场景名是区分大小写的,如果加载的场景名不一致或者不存在,则无法进行加载。
咱们这个游戏先告一段落,简单讲解了一下思路,接下来咱们用真正可以使用的例子继续学习。