CocosCreator+TS实现“祖马”小游戏中的路径角色回退效果
CocosCreator+TS实现“祖马”小游戏中的路径角色回退效果。

如图打掉中间的珠子,后面的珠子会回退并无缝连接上前面的珠子。
思路:
大概就是,击中的珠子消除前,先做位置置换。
位置直接交换就像“闪现”一样肯定不行,需要的效果是沿着途中的路径线性回退。
有两种方案:
1.保存击中珠子以及之后的所有珠子的位置,后面的珠子逐个(遍历)位置移动
(无脑但是有点麻烦)
2.后面的珠子逐个向前移动,使用await promise连接起来,消失几个珠子就异步几次移动(比第一种方式更安全)
这里以一个简单的例子来实现效果:

import { _decorator, Color, color, Component, instantiate, Node, Prefab, Sprite, tween, v3 } from 'cc';
import { Monster } from './Monster';
const { ccclass, property } = _decorator;
@ccclass('MonsterMgr')
export class MonsterMgr extends Component {
@property(Prefab)
pre_monster:Prefab = null;
@property()
max_count:number = 50;
@property(Node)
birth_node:Node = null;
@property(Node)
end_node:Node = null;
back_list:number[] = [];
m_colors = [
color(255,255,255,255),
color(245,221,113,255),
color(200,240,99,255),
color(160,240,99,255),
];
exist_monster_arr:Node[] = [];
m_grap:number = 60;
m_spwan_index:number = -1;
m_speed:number = 100;
m_cur_color:Color = null;
public static Instance:MonsterMgr = null;
protected onLoad(): void {
if(!MonsterMgr.Instance){
MonsterMgr.Instance = this;
}
}
start() {
this.spwanMonster();
}
spwanMonster(){
this.m_spwan_index += 1;
if(this.m_spwan_index >= this.max_count)return;
let m = instantiate(this.pre_monster);
m.parent = this.node;
if(this.exist_monster_arr.length > 0){
m.position = v3(this.exist_monster_arr[0].position.x - this.m_grap,this.exist_monster_arr[0].position.y);
}else{
m.position = this.birth_node.position;
};
this.exist_monster_arr.unshift(m);
let groupIndex = Math.floor(this.m_spwan_index / 5);
let mIndex = this.m_spwan_index % 5;
if(mIndex == 0){
this.m_cur_color = this.m_colors[Math.floor(Math.random()* this.m_colors.length)];
}
m.getComponent(Monster).init(groupIndex,mIndex,this.m_speed);
m.getComponent(Sprite).color = this.m_cur_color;
this.scheduleOnce(()=>{
this.spwanMonster();
},0.001);
}
stopMove(){
for(let i = 0;i < this.exist_monster_arr.length;i++){
let m = this.exist_monster_arr[i];
if(m){
m.getComponent(Monster).pauseMove();
}
}
}
deathGroupMonster(group_index:number){
let start_m_index = this.max_count - 1 - group_index*5;
for(let i = start_m_index;i > start_m_index - 5;i--){
console.log("i = " + i);
let m = this.exist_monster_arr[i];
if(m){
console.log('death + ')
m.getComponent(Monster).death();
}
};
//判断是否需要移动
let can_move = false;
for(let j = start_m_index + 1;j < this.max_count;j++){
let m = this.exist_monster_arr[j];
if(m && !(m.getComponent(Monster).is_death)){
can_move = true;
}
}
if(can_move){
let index = 0;
this.checkBackMove(index,start_m_index);
}
}
async checkBackMove(index:number,start_m_index:number){
if(index >= 5){
return;
}
await this.backMove(start_m_index);
this.resetGroups();
this.checkBackMove(index + 1,start_m_index - 1);
}
backMove(start_m_index:number){
let move_promises:Promise<any>[] = [];
let temp_start_index = start_m_index + 5;
//需要移动5步
for(let j = temp_start_index;j > temp_start_index - 5;j--){
let front_m_index = j - 1;
let front_m = this.exist_monster_arr[front_m_index];
let front_m_pos = front_m.position.clone();
let cur_m = this.exist_monster_arr[j];
move_promises.push(new Promise<void>((resolve, reject) => {
tween(cur_m)
.to(1,{position:front_m_pos})
.call(()=>resolve())
.start()
}))
};
let up_front_m = this.exist_monster_arr[temp_start_index];
for(let k = temp_start_index - 5;k < temp_start_index;k++){
this.exist_monster_arr[k] = this.exist_monster_arr[k + 1];
};
this.exist_monster_arr[temp_start_index + 5] = up_front_m;
return Promise.all(move_promises);
}
resetGroups(){
let group_index = Math.floor(this.max_count / 5) - 1;
let mIndex = 5;
for(let i = 0;i < this.max_count;i++){
let m = this.exist_monster_arr[i];
mIndex = mIndex - 1;
if(mIndex < 0){
group_index = group_index - 1;
mIndex = 4;
}
if(m){
m.getComponent(Monster).resetGroup(group_index,mIndex);
}
}
}
update(deltaTime: number) {
}
}
管理怪物的生成和销毁(珠子的创建和回收)
import { _decorator, Component, Label, Node, tween, UIOpacity, v3, Vec3 } from 'cc';
import { MonsterMgr } from './MonsterMgr';
const { ccclass, property } = _decorator;
@ccclass('Monster')
export class Monster extends Component {
group_index:number = -1;
m_index:number = -1;
ismoving:boolean = true;
m_speed:number = 100;
temp_Vec:Vec3 = v3();
is_death:boolean = false;
init(groupIndex:number,mIndex:number,mSpeed:number){
this.group_index = groupIndex;
this.m_index = mIndex;
this.m_speed = mSpeed;
this.node.getChildByName('g').getComponent(Label).string = 'g:'+this.group_index;
this.node.getChildByName('m').getComponent(Label).string = 'm:'+this.m_index;
this.is_death = false;
}
resetGroup(groupIndex:number,mIndex:number){
this.group_index = groupIndex;
this.m_index = mIndex;
this.node.getChildByName('g').getComponent(Label).string = 'g:'+this.group_index;
this.node.getChildByName('m').getComponent(Label).string = 'm:'+this.m_index;
}
pauseMove(){
this.ismoving = false;
}
reuseMove(){
this.ismoving = true;
}
protected start(): void {
this.node.on(Node.EventType.TOUCH_START,this.onTouch,this);
}
onTouch(){
if(!this.ismoving)return;
if(this.is_death)return;
MonsterMgr.Instance.stopMove();
MonsterMgr.Instance.deathGroupMonster(this.group_index);
}
death(){
this.is_death = true;
tween(this.node.getComponent(UIOpacity))
.to(0.5,{opacity:0})
.start();
}
protected update(dt: number): void {
if(!this.ismoving)return;
this.temp_Vec.x = this.node.position.x + this.m_speed*dt;
this.temp_Vec.y = this.node.position.y;
this.temp_Vec.z = 0;
this.node.position = this.temp_Vec;
}
}
核心逻辑是checkBackMove和backMove
这里由于把monster存在了一个类似链表中,所以每次异步移动后更新一下链表。
至于resetGroups,可以自定义一些需要更新的数据在里面,方便monster属性的扩展。
浙公网安备 33010602011771号