Funcode学习笔记:完成Run、Jump、Idle等动作【后续更新Roll、Attack动作的实现】【By Myself】

先来实现Run和Idle动作吧;

【以下是本菜在写游戏时犯下的一些错误,以及一些灵感,即如何解决逻辑错误的;】

首先,先大概说一下我们的愿景是什么,当我们按下A键时,人物向左边运动,且播放向左边运动的动画,我们按下右键时人物向右边运动,且播放一个向右运动的动画,当我们不按键时,人物不会有运动,且播放Idle动画;

这个思路很好实现;

我们先定义以下变量和方法;

class King {
	bool PressA;//PressA=1时说明A键按下
    bool PressD;//PressD=1时说明D键按下
public:
	CAnimationSprite *KingRed;    

	void KeyChangeSpeedLeft() {
		KingRed->//设置精灵向左速度    
	}
}   void KeyChangeSpeedRight(){        
		KingRed->//设置精灵向右的速度    
	}        
	void SpritePlayAnimation(bool AnimatiomRection){//根据PressA和PressD来播放动画        
		if(AnimationRection) //播放向左的动画         
		else //播放向右的动画    
	}
}

思路是这样的:

当按下就AD键时,funcode自动调用OnKeyDown函数,然后我们在OnKeyDown函数里按照按下键,调用以上3个函数,以实现左右移动和动画的播放;

但是,这里会有一个bug,即一般游戏中,能很明显地感觉到AD键对速度不是相互覆盖的关系,而是相互博弈的关系【什么意思呢?很简单,上面的函数我们可以知道,按下A键后,就直接给人物一个向左的速度,而不会去管是否按下D键,这就是相互覆盖的关系,但是正常游戏为了增加玩家的手感,都会采用AD键博弈的关系,即按下A键后,不是直接让人物直接向左移动,而是给人物一个向左的速度,如果此时D键是按下的,也给了人物一个向右的速度,那么两个速度会相结合,使人物速度为0】;

f702d48257a641909a08b0effdf4cd6f.png

 8bc8b1727b3e466e93334090c414c0ca.png

明白了真正的需求,那么我们就得想办法怎么去实现;

本菜查了funcode的API发现没有给某个方向直接加上一个速度的函数,而只有直接设置某个方向的速度;

这里有一个方法,即我们定义一个变量LevelSt,这个变量的作用是AD键博弈关系和人物速度之间的一个桥梁;

LevelSt有3种状态,Level=1时,说明人物要向右移动;

LevelSt=0时,说明人物水平无速度;

LevelSt=-1,说明人物要向左移动;

当我们按下A键时LevelSt--,当A键弹起时LevelSt++;

当我们按下D键时LevelSt++,当D键弹起时LevelSt--;

然后我们再定义一个函数LevelStChangeLevelSpeed()

如字面意思,这个函数是通过LevelSt改变水平Speed状态的;

我们对上面的King类进行优化:

class King {
	bool PressA;
    bool PressD;    
    int LevelSt; 
public:
	CAnimationSprite *KingRed;        
	King() :LevelSt(0), PressA(0), PressD(0) { 
		KingRed = new CAnimationSprite("KingRed");
	}//构造函数    
	~King(){
		delete KingRed;    
	}        
	void KeyAChangeLevelSt(int Measure){
		LevelSt+=Measure;    
	}        
	void KeyDChangeLevelSt(int Measure){
		LevelSt+=Measure;    
	}        
	void LevelStChangeLevelSpeed(){
		if(LevelSt==1) //设置向右的速度        
		else if(LevelSt==0) //设置向左的速度
		else //设置零速度    
	}        
	void SpritePlayAnimation(){//根据LevelSt                
		if(LevelSt==1) //播放向左的动画        
		else if(LevelSt==-1) //播放向右的动画        
		else //播放Idle动画,毕竟速度为0嘛    
	}
}

这个时候我们将各各函数放到CGameMain类中的各各函数中即可;

因为要受键影响,所以KeyDChangeLevelSt和KeyAChangeLevelSt函数应该放到OnKeyDown函数和OnKeyUp函数中,而函数播放是每时每刻在进行的,所以应该放到GameRun函数中;

然后按照以上框架写上写代码;

...bug出现了,因为我们每时每刻都在调用SpritePlayAnimation函数,所以一直在调用播放动画的函数,因为没次调用播放动画的函数,所以都是从第一帧开始播放的,所以我们是看不到一连串图像构成的动画的,而是只能看到第一帧图;

那么我们该怎么修该这个bug呢??

有一个思路是这样的,当我们播放完动画后再重新播放就行了;

那么我们该怎么判断动画是否播放完了呢?

funcode还算是有点用哈,它提供了一个获取当前播放动画的名字的函数;

这里啰嗦几句,说一下什么是动画的名字;

动画的名字是程序与制作好的动画的接口,当我们要在程序中调用对应的动画时,对应的接口就是动画的名字,动画的名字可以在funcode界面制作动画时自定义;

funcode提供了一个函数:AnimateSpritePlayAnimation(char* AnimationName,bool IsRestore);

第一个参数就是动画的名字,第二个是一个选择播放完此动画是否回到原先的动画;

好,回归正题;

我们可以用funcode提供的函数:char *GetAnimateSpriteAnimationName();来获取当前动画的名字;

本菜的逻辑是这样的:

先获取当前动画的名字,如果当前的动画名字不等于我们需要播放的动画的名字,那么我们就播放我们需要播放的动画,反之我们不播放;

比如,我们当前要播放向右移动的动画,然后我程序发现正在播放向右移动的动画,那么我们为了不重新播放导致卡帧,我们就不播放。

注意,实现这套逻辑要将IsRestore参数调为1,即我们播放完该动画后要回到之前的动画;

那么下面是我们优化后的类:

class King {

	bool PressA;
	bool PressD;
	int LevelSt;

public:
	CAnimationSprite *KingRed;

	King() :LevelSt(0), PressA(0), PressD(0) {
		KingRed = new CAnimationSprite("KingRed");
	}//构造函数    

	void KeyAChangeLevelSt(int Measure) {
		LevelSt += Measure;
	}

	void KeyDChangeLevelSt(int Measure) {
		LevelSt += Measure;
	}

	void LevelStChangeLevelSpeed() {
		if (LevelSt == 1) //设置向右的速度        
		else if (LevelSt == 0) //设置向左的速度        
		else //设置零速度    
	}        

	void SpritePlayAnimation() {//根据LevelSt                
		static std::string AnimationName;
		AnimationName = KingRed->GetAnimateSpriteAnimationName();

		if (LevelSt == 1 && AnimationName != KingRunLeft) //播放向左的动画        
		else if (LevelSt == -1 && AnimationName != KingRunRight) //播放向右的动画        
		else if (AnimationName != KingIdle)//播放Idle动画,毕竟速度为0嘛    
	}
}

那么下面来实现Jump动作了;

这个的话,比较好弄。

因为现在动作多了起来,我们为了后期继续写下去,我们定义一些变量来记录当前的状态,因为有一些动作是要建立在一些状态之上才能调用的;

比如跳跃要在处于地面上的基础之上,Run动作要在处于地面的基础之上【除非你的角色是斗宗强者】

所以我们要新加以下变量:

int VertSt;//垂直速度>0时VertSt=1,垂直速度<0时,VertSt=-1,垂直速度为0时VertSt=0;
bool IsGround;//当位于地面上时IsGround=1,否则为0

先说一下我们的愿景:按下K时给人物一个向上的速度,如果角色垂直速度>0,那么我们就播放Jump动画,如果垂直速度<0那么我们播放Fall函数;

那么我们怎么判断是否在地面上呢?

我们可以用写一个函数JudIsGround(float Time);我们可以利用funcode中GameRun函数提供的一个参数fDeltaTime,这个参数是上一帧到这一帧之间的时间差,我们可以将这个变量传入到JudIsGround中:

我们先在JudIsGround函数中定义一个static变量OnGroundTime;

当垂直速度为0时,即VertSt=0时,我们OnGroundTime+=fDeltaTime;

当OnGroundTime>=0.05时,我们认为人物在地面上,即将IsGround=1;

如果垂直速度不等于0,那么IsGround=0;

然后我们要来实现跳跃动作;

首先要在funcode界面给人物一个重力:

c56dfe3ac38c4f0a93cd21a2d1941484.png

这个其实很好弄,当在IsGround=1且按下K键时,我们给人物一个向上的速度;

因为有重力,所以速度会递减,如何我们在GameRun中不断用funcode提供的获取垂直速度的函数来判断VertSt的数值,如何不断用VertSt判断该播放那个动画:

d592d031bbee4d6081360afa736c1153.png

 b3025f31c9df461aa80142a72c8126b6.png

 如果VertSt>0播放Jump,如果<0播放Fall;

那么优化后的类如下:

class King{
    bool PressA;
    bool PressD;
    int LevelSt;
    int VertSt;
    bool IsGround;
    
public:
    CAnimationSprite *KingRed;
    
    King():LevelSt(0),PressA(0),PressD(0){
        KingRed=new CAnimationSprite("KingRed");
    }//构造函数
    
    void KeyAChangeLevelSt(int Measure){
        LevelSt+=Measure;
    }
    
    void KeyDChangeLevelSt(int Measure){
        LevelSt+=Measure;
    }
    
    void LevelStChangeLevelSpeed(){
        if(LevelSt==1) //设置向右的速度
        else if(LevelSt==0) //设置向左的速度
        else //设置零速度
    }
    
    void JudIsGrond(float Time){
        
        static float OnGroundTime;
        
        if(VertSt==0) OnGroundTime+=Time;
        else IsGround=0;
        
        if(OnGroundTime>=0.05){
            IsGround=1;
            OnGroundTime=0;
        }
    }
    
    void VertSpeedChangeVertSt(){
        
        static SpeedY;
        
        SpeedY=KingRed->GetSpriteLinearVelocityY();
        
        if(SpeedY<0) VertSt=1;//funcode中向上时速度小于0
        else if(SpeedY>0) VertSt=-1;
        else VertSt=0;
    }
    
    void SpritePlayAnimation(){//根据LevelSt
        
        static std::string AnimationName;
        
        AnimationName=KingRed->GetAnimateSpriteAnimationName();
        
        if(IsGround==0){//说明在地面上
        
            if(LevelSt==1&&AnimationName!=KingRunLeft) //播放向左的动画
            else if(LevelSt==-1&&AnimationName!=KingRunRight) //播放向右的动画
            else if(AnimationName!=KingIdle)//播放Idle动画,毕竟速度为0嘛
        }else{
            
            if(VertSt==1&&AnimationName!="KingJump") //播放向上跳跃的动画
            else if(Vert==-1&&AnimationName!="KingFall")//播放Fall动画
            
        }
        
    }
}

大概的思路就是如此;

以后会更新Attack和Roll动作;

为了可以拓展出更多的动画,我们必须写一些具有普适性的函数,和一些有普适性的变量,以及播放动画遍历的函数尽量路径统一,否则会多出很多bug,写出一坨屎山;

这里本菜总结出一个大概的框架,即人物状态因该为如何:

等待输入->播放动画->状态判断和状态校正->等待输入;

  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值