<p> 这种模式很常见,也可以衍生类似的抽奖系统之类,以水浒传为例,市面上的水浒传有多种表现形式,我见到过主要的两种:
一种是分为15个格子,每个格子之内做单独自己的展示,在动画开始的时候会把格子里面的图片换成一个类似具体物体动起来的效果,但是实际上只是一张模糊的图片,这种模式最简答,一点难度都没有;
另外一种模式,整个旋转界面分成五列,以列的单位进行旋转,然后逐列停下,每列三个物体,旋转的过程中随机展示对应的物品。
<p> 下面说一下我的做法,思路共享。
显示物体替换,这个很简单,就是最简单的换肤,直接随机记得物件的id,然后根据id取对应的皮肤设置到组件上面就好。单元格移动,我们都知道像水浒传这种要求的是3行5列,水果拉霸可能是3行4列,植物大战僵尸3行3列之类,都是类似的,看似要按照列来进行设计,其实采用的还是每一个物体进行面向对象的设计。
首先,观察游戏没有开始的时候,每一个物体都在自己的点位上面,那么可以见到的有15个点位,然后转动的时候,每列里面的子物体会一次移动到相对自己的下一个点位,从而表现的像整体的移动,此时每一列有3个显而易见的坐标点,然后再最上面的点和最下面的点之外分别设置一个隐藏的起点和终点,当每一个物体移动到终点之后,立即将它的点位设置到起点位置,然后开始了新的一次运动。这里公有五个点,也就会将竖直方向分出四个时间段,每个时间段的运行时间自己可以设置,然后累计的转几个周期也可以进行自己设置。在第一圈根据自己当前点,获得第一圈里面那一节自己的运行时间,最后一圈也是类似,根据需求设置时间,达到效果。
下面附上参考代码:
module wm {
export class MoveObj extends Sprite {
private cfgItems: any;
/**每次新出现的时候需要变换的图片,第一张是初始的 上次结果或者默认,最后一张是 最终结果的图片 */
private skinArr: number[] = []
/**其实就是它所在的第1行 */
private destination: number = 1;
/**要转几圈 圈数会影响转动的时间 */
private moveCount: number = 0;
private myData: MoveData;
/**显示对象 */
private img: Laya.Image;
/**当前转到第几圈 */
private circleIndex: number = 0;
private winAni: Laya.Animation;
private curItemId: number = 0;
constructor() {
super();
this.cfgItems = Laya.loader.getRes("config/watermargin.json")
}
public setSkinArr(arr: number[]): void {
this.skinArr = arr
}
public setSkinNow(itemId: number) {
this.curItemId = itemId
this.img.skin = this.getSkinById(itemId);
this.img.gray = false;
this.winAni.visible = false
}
public initData(data: MoveData, imgObg: Laya.Image, moveCount: number, iten: number): void {
this.myData = data;
this.circleIndex = 0;
this.img = imgObg;
this.moveCount = moveCount
this.winAni = new Laya.Animation();
this.img.addChild(this.winAni);
this.curItemId = iten
this.img.skin = this.getSkinById(this.curItemId);
}
/**立即移动 */
public moveActionNow(): void {
this.circleIndex++
this.img.skin = this.getSkinById(this.skinArr[this.circleIndex - 1]);
this.curItemId = this.skinArr[this.circleIndex - 1]
let moveTime: number = 85;
//第一圈和最后一圈特殊处理
if (this.circleIndex == 1) {
moveTime = (2 - this.destination) * 85;
Laya.Tween.to(this.img, { y: this.myData.points[2] }, moveTime, null, Laya.Handler.create(this, () => {
this.img.y = this.myData.points[0];//回到初始位置,准备下一次移动
this.moveActionNow();
}));
} else if (this.circleIndex == this.moveCount) {
moveTime = (this.destination) * 85;
Laya.Tween.to(this.img, { y: this.myData.points[this.destination] }, moveTime, Laya.Ease.backOut, Laya.Handler.create(this, () => {
this.circleIndex = 0;
if (this.myData.moveId == 3) {//最后一个移动结束了
}
}));
} else {
moveTime = 85 * 2
Laya.Tween.to(this.img, { y: this.myData.points[2] }, moveTime, null, Laya.Handler.create(this, () => {
this.img.y = this.myData.points[0];//回到初始位置,准备下一次移动
this.moveActionNow();
}));
}
}
public setGray(gray: boolean): void {
this.img.gray = gray;
}
public setStartState(): void {
this.winAni.visible = false
this.img.skin = this.getSkinById(this.curItemId);
Laya.timer.clear(this, this.loopWinFrameAlpha)
this.winAni.stop();
}
public playWinAni(): void {
let url: string = this.cfgItems[this.curItemId].aniAtlas
this.winAni.pos(this.cfgItems[this.curItemId].x, this.cfgItems[this.curItemId].y);
this.winAni.frames = [];
this.winAni.loadAtlas(url, Laya.Handler.create(this, this.onGoldAniLoaded));
Laya.timer.loop(10, this, this.loopWinFrameAlpha)
}
private onGoldAniLoaded(): void {
this.winAni.visible = true
this.img.skin = ""
this.winAni.play(0, false)
}
public getCurItemId(): number {
return this.curItemId
}
private loopWinFrameAlpha(): void {
}
private getSkinById(itemId: number): string {
let resStr: string = this.cfgItems[itemId].skin;
return resStr
}
}
}
上面就是每个移动物体对应的对象类,上面是LayaAir的ts代码。
在做界面的时候,为了不让玩家看到因残的起点和终点,只需要将这15个子对象放到Panel组件内,然后设置好panel组件的边界,这样就完美实现了。
这个功能可以实现很多游戏里面的功能,例如抽奖,随机奖励之类。
如果你见到这篇笔记有更好的实现思路,欢迎留言交流。
帝国程序员万岁!!!!!!