在上一篇文章中,我们已经将游戏的场景基本搭建完毕,接下来我们就可以为游戏编写代码并实现相关的核心逻辑了。
政安晨的个人主页:政安晨
欢迎 👍点赞✍评论⭐收藏
收录专栏: 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~2let 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; // 获取目标节点的nameif (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加载的场景名是区分大小写的,如果加载的场景名不一致或者不存在,则无法进行加载。
咱们这个游戏先告一段落,简单讲解了一下思路,接下来咱们用真正可以使用的例子继续学习。