现在的位置: 首页 > 综合 > 正文

用Direct3D设计二维横版过关动作类游戏的角色运动(双倍速、二段跳)

2013年08月29日 ⁄ 综合 ⁄ 共 2852字 ⁄ 字号 评论关闭

用Direct3D设计二维横版过关动作类游戏的角色运动

演示程序下载地址:http://download.csdn.net/detail/jiangcaiyang123/4278008

虽然大家使用Direct3D的初衷是编写三维的游戏,而且看起来三维的游戏比起二维的游戏诱惑更大,但是我还是坚持先研究二维的游戏,这样我可以通过二维游戏的编写了解到游戏的编写流程,以后进行三维游戏的过渡会比较简单。

之前我在一些群里提问了,提问的内容是怎样设计一个角色的运动系统,它可以实现二段跳,可以以单倍速度运动,也可以以双倍速度运动,也没有高手能够帮助我,所以自己还是自力更生吧,自己制作角色的运动系统。

角色的运动系统说起来很简单,其实还要考虑很多的问题。在设计的时候一方面要考虑效率的问题,另一方面要考虑设计能够简单明了,以后自己维护甚至别人维护不至于迷惑。我以前也完成了一个类似的系统,使用Lua编写的,现在转向了C++,效率的瓶颈就没有那么严重了,而且我另外开了个线程,专门负责所有地图块单元的运动,不过我还是小心谨慎的一遍又一遍的设计,希望能够达到更好的效果。

对于用同样困惑的同学,我也只能点到为止,因为大家遇到的情况都不相同,我也不好具体地分析每一种情况。

首先假设有两个函数KeyDown( )和KeyUp( ),它们表示按键按下和弹起。CPlayerUnit表示玩家(角色)所在的单元的类。那么实现左、右的移动可以这样分析:

	if ( KeyDown( 左 ) )
	{
		if ( 碰撞检测 == 没有碰撞 && 边界检测 == 没有越过边界 )
		{
			Player->GoLeft( );
		}
	}
	if ( KeyDown( 右 ) )
	{
		if ( 碰撞检测 == 没有碰撞 && 边界检测 == 没有越过边界 )
		{
			Player->GoRight( );
		}
	}

这里说一下,为什么将碰撞检测放在前面呢?以前我的做法是将边界检测放在前面的,但是后面我想了想,碰撞检测失败的几率比边界检测要大得多(地图上的砖块比较多),由于&&短路的性质,当检测到碰撞也就不会执行边界检测了。所以这样做平均时间复杂度分析要划算一些。

这样简单的左右移动就完成了,接下来要完成的是双倍速的左右移动的检测。
不知道大家以前玩过《忍者猫》没有?对了《激龟快打》也是,这些游戏的操作是这样的,如果玩家在很短的时间内按下了两次左键或者是右键,那么角色会以两倍速运动,角色动画也会不一样。按照这样的行为我们来想想究竟该如何实现它。

其实可以这么想,调用系统函数获取时间,当第一次按下左、右键时,记录下此时的时间,松手后第二次按下按下左、右键时,通过当前时间与第一次时间的比较,如果超过了规定的数值(比如说500毫秒),那么还是原来的速度,如果没有超过该数值,那么可以以双倍速运动。增加的语句如下所示:

	←bLock = false;		// 初始化的时候赋值为假
	←speed = 单倍速;
	←lastKeyDownTime = 0;
	
	if ( KeyDown( 左 ) )
	{
		if ( 碰撞检测 == 没有碰撞 && 边界检测 == 没有越过边界 )
		{
			if ( !bLock )
			{
				if ( std::clock( ) - lastKeyDownTime < 500 )
				{
					speed = 双倍速;
				}
				else
				{
					speed = 单倍速;
				}
				lastKeyDownTime = std::clock( );
				bLock = true;
			}
			Player->GoLeft( speed );
		}
	}
	if ( KeyUp( 左 ) )
	{
		bLock = false;
	}
	if ( KeyDown( 右 ) )
	{
		…………与上面类似
	}
	if ( KeyUp( 左 ) )
	{
		…………与上面类似
	}


这里注意的是,由于按键的响应是实时调用的,要使用一个锁进行锁住,即第一次记录时间,以后相同的行为不要记录时间。

接下来介绍一下跳跃功能的实现。弹跳和下落是相对的,不同的是,弹跳仅当按下了按键时才会触发,而在弹跳触发的过程中,下落不会被触发,不过其余的情况下它总是会被触发。那么首先来实现一个小小的跳跃功能吧。

	←bLock = false;			// 跳跃锁
	←jumpHeight = 0;		// 跳跃的高度
	
	if ( KeyUp( C键 ) )
	{
		bLock = true;
		jumpHeight = 三个地图块的高度
	}
	if ( KeyDown( C键 ) )
	{
		if ( 向上碰撞检测 == 没有碰撞 && 边界检测 == 没有超过边界 )
		{
			bLock = false;
			将jumpHeight递减,Y递增
			if ( jumpHeight == 0 )
			{
				Fall( );		// 执行下落函数
			}
		}
		else
		{
			bLock = true;
			jumpHeight = 三个地图块的高度
			Fall( );			// 执行下落函数
		}
	}
	
	这里Fall( )函数的定义是这样的:
	void Fall( void )
	{
		if ( 没有检测到与下面砖块的碰撞 )
		{
			Y递减
		}
	}


这里也用到了锁的概念。由于这些操作都是实时的,所以锁的存在可以避免一些数值被重复运算。
就这样完成了一个简单的一段跳运算,要实现二段跳,还要再添加一些代码才行。首先想想,如果角色要支持二段跳,那么必须设立一个名为jumpTime的变量,记录了跳跃的次数。其次,在第一次按下C键时,jumpTime要减一,其中要使用锁来避免减得过多。第二次按下C键时,jumpTime继续减一,直到减到0,此时按下C键没有任何反应,直接进入Fall( )过程。下面的伪代码可以帮助理解其过程。

	←bLock = false;			// 跳跃锁
	←jumpHeight = 0;		// 跳跃的高度
	jumpTime = 2;			// 二段跳
	
	if ( KeyUp( C键 ) )
	{
		bLock = true;
		jumpHeight = 三个地图块的高度
		if ( jumpTime == 2 ) jumpTime = 0;		// ※待会儿会解释
		Fall( );					// 执行下落函数
	}
	if ( KeyDown( C键 ) )
	{
		if ( 向上碰撞检测 == 没有碰撞 && 边界检测 == 没有超过边界 )
		{
			bLock = false;
			if ( jumpTime > 0 )		// 还可以跳跃
			{
				将jumpHeight递减,Y递增
				if ( jumpHeight == 0 )
				{
					Fall( );		// 执行下落函数
				}
			}
			else
			{
				Fall( );			// 执行下落函数
			}
		}
		else
		{
			bLock = true;
			jumpHeight = 三个地图块的高度
			if ( jumpTime == 2 ) jumpTime = 0;		// ※待会儿会解释
			Fall( );				// 执行下落函数
		}
	}


在这里※部分,描述了一种情况:当玩家没有跳跃而落下的时候,在空中可否允许跳跃呢?答案是否定的。我在很多的游戏中看到不允许跳跃的情况,那么一个简单的判定方法是如果落下的时候jumpTime为2(即没有启动过跳跃程序),JumpTime应该为0,这样就避免了一个逻辑上的Bug。

我现在在实验的这个程序是仿照几年前Lizsoft的Fortune Summoners游戏进行制作的。该游戏制作精美,动感十足,看得出游戏公司几年的锤炼是富有成效的。一下是该游戏的截图:


我制作的游戏演示程序也一并展示出来,这里恳请高手给出点评。

演示程序下载地址:http://download.csdn.net/detail/jiangcaiyang123/4278008

抱歉!评论已关闭.