Egret社区

如何实现跳跃的动作

2014-8-8 14:46
888014
2银子
大家好,我是新手开发者pd,我现在在尝试开发一款跑酷类游戏,需要实现角色跳跃

在cocos2dx中,简单的新建一个jump action添加到精灵里面就可以实现跳跃动作了
但是在egret里面,貌似只有Tween可以移动object,就我现在对egret有限的了解,只能实现匀速升起落下,但是离近似有重力的跳跃还有差距

窃问众神如何实现跳跃的动作呢?
感谢您任何类型的帮助

分享到 :
5 人收藏

14 个回复

倒序浏览
pyed  登堂入室 | 2014-8-17 00:51:47
本帖最后由 pyed 于 2014-8-17 00:59 编辑

我自己实现了这一功能,其实配合着事件机制,是很容易实现的。
首先我使用Tween来凑合着表示跳跃,看Tween源码的时候发现
to( props: Object, duration: number, ease: egret.Ease ) → { egret.Tween }
ease   egret.Ease    缓动算法
这很可能就是实现一些高级一点移动的方式,但是在API文档中找不到egret.Ease类,而且在源码中可以看到to函数调用_addStep,但是_addStep中没有处理ease的代码,所以没有现成的东西可以用来表示跳跃这种带有重力加速度的动作。
public to(props, duration:number, ease = undefined):Tween {
            if (isNaN(duration) || duration < 0) {
                duration = 0;
            }
            return this._addStep({d: duration || 0, p0: this._cloneProps(this._curQueueProps), e: ease, p1: this._cloneProps(this._appendQueueProps(props))});
        }


private _addStep(o):Tween {
            if (o.d > 0) {
                this._steps.push(o);
                o.t = this.duration;
                this.duration += o.d;
            }
            return this;
        }

----------------------

但其实,只用少量的代码就能够实现跳跃这种动作。
我们假设在场景类中有主角,并且主角在屏幕中间,以屏幕中间作为地面,向上跳跃
  1. private master:egret.Bitmap = new egret.Bitmap(RES.getRes("actor")); //表示主角
复制代码
并且,已经在stage上加上了点击监听的监听器
  1. this.stage.addEventListener(egret.TouchEvent.TOUCH_TAP, this.jump, this);
复制代码
在jump中给一个初始速度
private jump(evt:egret.TouchEvent):void {
        this.speed = 6;
        if(!this.master.hasEventListener(egret.Event.ENTER_FRAME))
            this.master.addEventListener(egret.Event.ENTER_FRAME, this.fly, this);
    }

    private fly():void {
        this.master.x += this.speed;
        this.speed -= 0.3;
        if(this.master.x < this.stage.stageWidth/2) {
            this.master.x = this.stage.stageWidth/2;
            this.speed = 0;
            this.master.removeEventListener(egret.Event.ENTER_FRAME, this.fly, this);
        }
    }




这样点击后,master就会跳起来,然后重力下落。这样实现能直接实现多段跳,并且不同在Tween中进行复杂设定和操作。
这个问题也算是解决了吧。


pyed  登堂入室 | 2014-8-17 00:59:54
本帖最后由 pyed 于 2014-8-21 15:35 编辑

监听ENTER_FRAME消息来更新container里面的元素,会遇到几个问题:最重要的一个问题是:当设备性能不佳时,每一帧的消耗的时间不同,于是乎跳跃的时间和高度就变换很大。
这里援引wander的解答以及wander在github上的解答
几乎完美的解决了这个问题
pyed  登堂入室 | 2014-8-21 20:09:46

RE: 如何实现跳跃的动作

本帖最后由 pyed 于 2014-8-21 20:10 编辑

根据里面网址中的方法我们可以得出两帧之间的间隔时间dt(当然这个间隔你可以自己计算,只用记录上一帧的时间和这一帧的时间只差即可)

FPS为每一秒行进的帧数,单位为f/s, FPS=1000/dt,因为dt是帧的时间间隔,1秒钟(即1000毫秒)有多少帧为1000/dt(dt单位为毫秒)。由于正常的FPS为60f/s,那么我们设置一个运动速度的补偿,offset=60/FPS。因为正常情况下游戏FPS默认60帧,也就是说1秒钟60帧,假设1秒钟对象移动120像素,那么就是说1帧移动2像素。当游戏FPS降到30帧的时候,就是说1秒钟30帧,这时如果还要1秒钟移动120像素的话,一帧要移动4像素,也就是2*offset


也就是说如果要匀速移动对象的话,算出每一帧的offset然后乘以速度作为每一帧的移动距离就可以了。
但是在做变速运动的时候,比如现在开发的自由落体的时候,就会有问题。不能仅仅考虑offset对速度的影响,对加速度也要有所影响。

假设故事背景是实现一个带重力的跳跃的前半段,跳到最高点速度变为0
IMG_20140821_200156.jpg

上图是证明过程,图太小抱歉

简单总结一下就是,get到现在的fps,算出每一帧的offset,在跳起以后,速度调整为v*offset,加速度调整为a*offset*offset。
修改代码如下:

private offset:number = 1;
public updateOffset(sp:number):void {
        this.offset = sp;
    }

private jump(evt:egret.TouchEvent):void {
        this.speed = 6 * this.offset;
        if(!this.master.hasEventListener(egret.Event.ENTER_FRAME))
            this.master.addEventListener(egret.Event.ENTER_FRAME, this.fly, this);
    }

    private fly():void {
        this.master.x += this.speed;
        this.speed -= 0.3 * this.offset * this.offset;
        if(this.master.x < this.stage.stageWidth/2) {
            this.master.x = this.stage.stageWidth/2;
            this.speed = 0;
            this.master.removeEventListener(egret.Event.ENTER_FRAME, this.fly, this);
        }
    }

经过测试,fps很低的时候依然可以保证重力跳跃的效果
pyed  登堂入室 | 2014-8-27 13:20:17
最终版
用Tween来实现带重力跳跃

思路:使用缓动算法egret.Ease.quartIn  egret.Ease.quartOut
写法:egret.Tween.get(obj).to({y:height},time, egret.Ease.quartOut).to({y:ground}, time, egret.Ease.quartIn);
这么写的话不用考虑游戏fps变动对跳跃速度的影响(这么写就已经适应了不同fps下的游戏),既不用计算speedOffset,也不用自己写跳跃位置的更新函数。简单方便有效(为啥一开始我不知道呢),这里的确是带重力加速度的跳跃,因为egret.Ease.quartIn  egret.Ease.quartOut,是根据时间的平方来计算距离的,也就是(a*t^2)/2,只用设置合适的高度和合适的运动时间让他看起来像重力加速度即可。


实现虽然简单,但是如果要实现二段跳该怎么办呢?这里,二段跳就不像自己实现重力跳跃那样可以简单的重新赋个初速度就行了。这里我们应该进行如下操作。
  • egret.Tween.removeTweens(obj)把obj上的运动除去
  • 加上向上跳起的新运动
  • 加上向下落地的新运动,这里下落的时间不等于time(毕竟跳的比原来更高了)而是等于time*Math.sqrt(currentHeight / height),这里Math.sqrt是求平方根,currentHeight是当前高度。这个公式可以自行推理一下


于是jump函数可以这样写

class actor extends egret.Bitmap {
    /*省略constructor和其他的函数
    *
    */

    action:egret.Tween = null; //这样写可以实现暂停和恢复的功能
    public jump():void {
        egret.Tween.removeTweens(this);
        var currentY:number = this.y;
        var currentHeight:number = this.ground - this.y;
        var downTime:number = this.time * Math.sqrt(currentHeight / this.height);
        this.action = egret.Tween.get(this).to({y:currentY - this.height}, this.time, egret.Ease.quartOut).to({y:this.ground}, downTime, egret.Ease.quartIn);
    }

    public pauseJump():void {
        this.action.setPaused(true);
        //this.action.pause(); 失效了,不知道为什么
    }

    public continueJump():void {
        this.action.setPaused(false);
        //this.action.play(); 也失效了,不知道为什么
    }
}
        
pyed  登堂入室 | 2014-8-17 01:00:49
songsharp  斑竹 | 2014-8-18 11:02:38
亲,有你这样来 自问自答的吗!
谢谢分享!
pyed  登堂入室 | 2014-8-18 16:08:23
songsharp 发表于 2014-8-18 11:02
亲,有你这样来 自问自答的吗!
谢谢分享!

木有人回答嘛,只好自问自答咯,如果能给别人带来帮助,也算是值了
pyed  登堂入室 | 2014-8-18 16:15:38
本帖最后由 pyed 于 2014-8-18 16:53 编辑

实际上Egret中已经实现了Ease的缓动算法,详情见guoshaorui斑竹的教程点击这里预览一下效果

在API中没相应的信息,源码中也没有详细说明(本人水平有限)在egret的github里面有相应的官方example,里面应该有用到。请查看官方的github



其中egret.Ease.bounceOut效果非常出色
用法:egret.Tween.get(object).to({x:400, y:100}, 1000, egret.Ease.bounceOut);即可

dily3825002  社区管理员 | 2014-8-18 16:26:15
别忘了,选择解决
pplboy  斑竹 | 2014-8-18 16:46:02
及时,我也来试试~~
rainssong  登堂入室 | 2014-8-19 12:14:37
自己写一个简单的物理公式就可以解决不需要tween

hero.y+=hero.vy;
hero.vy+=0.98;
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|京网文[2014]0791-191号|京ICP证150115号|Egret社区 ( 京ICP备14025619号 )

Powered by Discuz! X3.4 © 2001-2019 Comsenz Inc.

返回顶部