最近遇到一个面试题,是个抽奖的玩法,于是就用CocosCreator2.4.11
和CocosCreator3.7.2
分别实现了一下。虽然整个看起来没有什么难度,但还是有些细节值得玩味的,干脆就记录一下,后面可以嵌入到游戏的运营活动里面去。
考题分析
题目要求
用Cocoscreator
实现一个简单的转盘抽奖页面。如下图:
要求。转盘启动后 每个数字依次顺时针被点亮为红色, 速度从每秒点亮20个数字(设定最高频率为20个/秒),持续1-3秒,逐步(中间频率自定义,每个频率的持续时间都是1-3秒之间,总频率数不得少于5个频率)降低到每秒点亮2个数字,并等待2秒后,落到最终设定的停止的位置的数字上,并显示为绿色。 转盘停止转动时 所有格子内的数字都要同时变成100。
每次启动转盘都从数字1开始,。
要求:
Cocoscreator
任意版本均可,
交付最终的所有工程项目文件。
完成效果
看看最后的效果吧!
2.4.11版本
3.7.2版本
步骤
要使用CocosCreator
实现一个简单的转盘抽奖页面,你需要遵循以下步骤:
创建项目: 首先,使用CocosCreator
创建一个新的项目。在编辑器中,使用Canvas节点,用作转盘的容器。
设计转盘: 将转盘的背景图片和数字图片添加到Turntable节点下。创建一个名为“TurntableItem
”的Prefab,包含一个数字精灵和一个文本标签。将这个Prefab放在Turntable节点下,并为每个数字分配一个唯一的位置。确保所有数字都是顺时针排列的。
编写脚本: 在项目中创建一个名为“TurntableController
”的脚本,并将其添加到Turntable节点。在脚本中,首先定义一些变量和常量来控制转盘的行为。例如,最大频率、最小频率、频率持续时间等。然后,在start
方法中,初始化转盘的状态。
控制转盘: 编写一个名为startTurntable
的方法来启动转盘。该方法首先将所有数字设置为初始状态。然后,使用schedule
函数来控制转盘的速度。根据要求,需要在1-3秒内从最大频率逐渐降低到最小频率。使用Math.floor(Math.random() * (max - min + 1)) + min
计算随机持续时间。在每个频率持续时间内,将数字按顺时针顺序点亮为红色。
停止转盘: 当达到最小频率时,等待2秒后,停止转盘并落到最终设定的停止位置的数字上。使用unschedule
函数来取消计划任务。将最终停止位置的数字显示为绿色,并将所有其他数字设置为100。
重置转盘: 为了确保每次启动转盘都从数字1开始,需要在startTurntable
方法开始时重置所有数字。
这个示例脚本可作为实现这个功能的参考。您可能需要根据实际项目需求对其进行修改和优化。同时,确保已经在项目中导入了必要的资源,如数字精灵和背景图片等。
核心算法
TurntableController.ts
这里只贴出2.4
版本,3.7
版本最后看源码地址
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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
| const {ccclass, property} = cc._decorator;
const GRID_SIZE: number = 80; const TURNTABLE_SIZE: number = 5; @ccclass export class TurntableController extends cc.Component {
@property({type: cc.Prefab}) public itemPrefab: cc.Prefab = null;
@property(cc.EditBox) public inputField: cc.EditBox = null; @property(cc.Button) public button: cc.Button = null;
private itemList: cc.Node[] = []; private currentNumberIndex: number = 0; private targetNumberIndex: number = 0; private remainNumberIndex: number = 0; private frequencyLevels: number[] = [20, 16, 12, 8, 4, 2]; private currentFrequencyLevel: number = 0; private currentBg: cc.Node = null;
protected start() { this.initTurntable(); }
private initTurntable() { const array = this.generateArrayWithZeros(TURNTABLE_SIZE); const offsetX = (GRID_SIZE * TURNTABLE_SIZE - GRID_SIZE) / 2; const offsetY = (GRID_SIZE * TURNTABLE_SIZE - GRID_SIZE) / 2 + 200; for (let i = 0; i < TURNTABLE_SIZE * TURNTABLE_SIZE; i++) { const itemNode = cc.instantiate(this.itemPrefab); itemNode.position = cc.v3( GRID_SIZE * (i % TURNTABLE_SIZE) - offsetX, offsetY - GRID_SIZE * Math.floor(i / TURNTABLE_SIZE), 0 ); this.node.addChild(itemNode); if (array[i]) { itemNode.getChildByName("num").getComponent(cc.Label).string = array[i] + ""; itemNode.name = array[i] + ""; this.itemList.push(itemNode); } else { itemNode.getChildByName("num").getComponent(cc.Label).string = ""; } } this.itemList.sort((a, b) => { return parseInt(a.name) - parseInt(b.name); }); }
private startTurntable() { this.button.interactable = false; this.resetNumbers(); this.currentNumberIndex = 0; this.currentFrequencyLevel = 0; this.remainNumberIndex = 0; const inputNumber: number = parseInt(this.inputField.string); this.targetNumberIndex = Number.isNaN(inputNumber) ? this.calculateTargetNumberIndex() : inputNumber - 1; this.remainNumberIndex = this.targetNumberIndex; if (Number.isNaN(inputNumber)) console.log("没有输入数字本次随机:", inputNumber); console.log("幸运数字:", this.targetNumberIndex + 1); this.schedule(this.updateTurntable, 1 / this.frequencyLevels[this.currentFrequencyLevel]); }
private resetNumbers() { this.currentBg = null; this.itemList.forEach((value, index) => { const bg = value.getChildByName("bg"); bg.color = cc.Color.WHITE; value.getChildByName("num").getComponent(cc.Label).string = value.name; }); }
private updateTurntable() { if (this.currentBg) this.currentBg.color = cc.Color.WHITE; this.highlightNumber(this.currentNumberIndex, cc.Color.RED); this.currentNumberIndex++; if (this.currentNumberIndex >= this.itemList.length) { this.currentNumberIndex = 0; this.currentFrequencyLevel++; if (this.currentFrequencyLevel >= this.frequencyLevels.length - 1) { this.unschedule(this.updateTurntable); this.schedule(this.lastTurntable, 1 / this.frequencyLevels[this.currentFrequencyLevel], this.remainNumberIndex); } else { this.unschedule(this.updateTurntable); this.schedule(this.updateTurntable, 1 / this.frequencyLevels[this.currentFrequencyLevel]); } } }
private lastTurntable() { if (this.currentBg) this.currentBg.color = cc.Color.WHITE; this.highlightNumber(this.currentNumberIndex, cc.Color.RED); this.remainNumberIndex--; console.log("转盘:", this.remainNumberIndex); this.currentNumberIndex++; if (this.remainNumberIndex < 0) { console.log("转盘结束:", this.remainNumberIndex); this.scheduleOnce(() => { this.stopTurntable(); }, 2); } }
private stopTurntable() { this.highlightNumber(this.targetNumberIndex, cc.Color.GREEN); this.setAllNumbersTo100(); this.button.interactable = true; }
private highlightNumber(index: number, color: cc.Color) { const numberNode = this.itemList[index]; if (numberNode) { const bg = numberNode.getChildByName("bg"); bg.color = color; this.currentBg = bg; } }
private calculateTargetNumberIndex(): number { return Math.floor(Math.random() * this.itemList.length); }
private setAllNumbersTo100() { for (const numberNode of this.itemList) { numberNode.getChildByName("num").getComponent(cc.Label).string = "100"; } }
private generateArrayWithZeros(size: number): number[] { const array = new Array(size * size).fill(0);
let currentNum = 1;
const fillLayer = (layer: number) => { const maxIndex = size - layer - 1;
for (let x = layer; x <= maxIndex; x++) { array[layer * size + x] = currentNum++; } for (let y = layer + 1; y <= maxIndex; y++) { array[y * size + maxIndex] = currentNum++; } for (let x = maxIndex - 1; x >= layer; x--) { array[maxIndex * size + x] = currentNum++; } for (let y = maxIndex - 1; y > layer; y--) { array[y * size + layer] = currentNum++; } };
for (let layer = 0; layer < 1; layer++) { fillLayer(layer); }
return array; } }
|
其中值得一提的是这个 生成0 的数组,这里面用了一个算法,便于拓展,比如 4X4 或者6X6 这样的
当然如果仅考虑这个题目,想省事可以直接这么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| generateArray(): number[] { const result = [ 1, 2, 3, 4, 5, 16, 0, 0, 0, 6, 15, 0, 0, 0, 7, 14, 0, 0, 0, 8, 13, 12, 11, 10, 9 ]; return result; }
const generatedArray = generateArray(); console.log(generatedArray);
|
文本输入限制
有一个细节这边也做了一个限制,就是文本的输入,限制EditBox
输入内容的组件,希望用户只能输入1-16之间的整数,组件实现:
您可以通过创建一个新的TypeScript
组件并为其添加一个事件监听器来实现这一功能。以下是一个简单示例,展示了如何在CocosCreator
中使用TypeScript
限制EditBox
输入内容:
- 首先,在您的项目中创建一个新的
TypeScript
脚本,并将其命名为InputRestrictor.ts
。
- 打开刚刚创建的
InputRestrictor.ts
文件,并替换其内容为以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const { ccclass, property } = cc._decorator;
@ccclass export class InputRestrictor extends cc.Component { @property(cc.EditBox) public inputField: cc.EditBox = null;
start() { this.inputField.string = ""; this.inputField.node.on("text-changed", this.onTextChanged, this); } private onTextChanged(editbox: cc.EditBox) { let num = parseInt(editbox.string); if (isNaN(num) || num < 1 || num > 16) { editbox.string = ""; } else { editbox.string = num.toString(); } } }
|
最后 源码地址 2.4.11 点击前往 3.7.2 点击前往
总结
总的来说这个功能包含了对按钮组件,输入文本组件,定时器,循环,以及布局和随机的考察。算是一个比较经典的一个玩法测试。