一款类似“恐龙快打”的 横版街机格斗游戏 该如何制作?| 一起来学习 顺便送源码码文不易,建议收藏学习

Posted 呆呆敲代码的小Y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一款类似“恐龙快打”的 横版街机格斗游戏 该如何制作?| 一起来学习 顺便送源码码文不易,建议收藏学习相关的知识,希望对你有一定的参考价值。

  • 📢博客主页:https://blog.csdn.net/zhangay1998
  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉
  • 📢未来很长,值得我们全力奔赴更美好的生活✨


📢引言

  • ❄️轻拾月光,听风声缱绻,如刹时花开。
  • 🌻浩浩九州,一文一墨皆是骄阳~
  • ☔️最近在上海天天下雨,风还很大!但还是要每天兢兢业业的当一个打工仔呀!
  • 🔓周末窝在家正在想着接下来要发一篇什么文章呢,突然来了一条消息
  • 🔔叮咚~ 原来是 憨憨的小云儿 发来的消息~
  • 小云儿👩:小Y哥哥,是不是最近天气不咋好,你也没心情制作游戏了呀😳!
  • 小Y(博主):咦~ 是不是你又想玩我制作的游戏了呀!才故意这么说的🙃
  • 小云儿👩:(偷笑)…哈哈哈嗝~ 小YY你变聪明了啊,不像是以前呆头呆脑的小Y哥哥了😏!
  • 小Y:Emma…我啥时候不聪明了!说吧,你最近又想学哪种类型的游戏了,说出来我听听🤪!
  • 小云儿👩:嘿嘿~小Y哥哥,我最近又对恐龙快打那样的横版格斗游戏玩法非常感兴趣,你能不能😛…
  • 小Y:恐龙快打呀~ 我之前也很喜欢玩!可是要从头做一款这个游戏确实有点麻烦唉🙄~
  • 小云儿👩:哎呀,小Y哥哥 你最棒啦~ 一定难不住你的对吗🧐!
  • 小Y:那好伐,那我想想办法给你整一个出来,我这就开始动手做👊!
  • 小云儿👩:好咧小Y哥哥,那我就慢慢期待了!老规矩!可以动手做,不阔以动脚做哦🍓!

🎬横版格斗游戏

  • 今天给大家带来一款横版格斗游戏,跟小时候玩过的恐龙快打一个类型😀!
  • 由于最近时间并不是很充裕,所以就在网上寻找一些可以用的素材😂
  • 突然发现了一款插件,这款插件居然完美符合我要做的一个横版格斗游戏所需的工具😜
  • 插件和本文涉及到的资源在文章最后都会提供源码工程的🤪!

简单游戏效果:



💫游戏制作思路

既然要做一款横版格斗游戏,那还是老规矩,先来整一下游戏思路😘!

我们先来想一下一个基本的横版格斗游戏都需要那些操作呢😊~

  • 首先有一个游戏场景那是必然的,还要有一个人物,是我们玩家可以操控的,下面称他为“主角”!
  • 然后这个场景需要根据玩家的位置,来进行摄像机的调整已达到一个视角的操控!
  • 接下来就是要有敌人,因为对打才是这种游戏玩法的核心所在!
  • 还有攻击方式,包括拳打脚踢和跳跃等
  • 敌人同样可以进行这些操作!
  • 还要有一个血条,在边缘显示我们的血量和当前打的敌人的血条!
  • 场景中还需要存在一些可交互的游戏物体
  • 比如可以打碎的箱子,可以吃的游戏物品
  • 还可以加一些可以拿起来的武器,比如刀枪棍棒等等!
  • 当然这些都是一个基本的游戏框架做出来的后话了!
  • 这样一说,感觉这个游戏做起来好像不难的样子哈,那我们就来的尝试一下吧
  • 如果遇到问题了,在进行补充说明!


🎉开始制作

首先打开Unity新建一个项目,博主开发这个使用的Unity2017.4.40

如果有小伙伴下载本文中的游戏资源,酌情使用Unity版本哦~

每个细节都写出来肯定是不现实哒,由于制作过程是在是又臭又长

所以文章中只把关键性操作和配置介绍出来,一起加油~


🏳️‍🌈第一步:新建项目,导入资源包

  • 前边说过了,我们现在从头制作一款游戏,如果模型之类的都要自己做那是不现实的
  • 而且模型这一块也会有专门的美术相关人员负责
  • 所以我们自己闲暇之余自己做游戏肯定要省去这一步
  • 直接寻找自己适合的资源即可!
  • 所以这里我们导入一个资源包

新建一个Unity项目,设置名字和对应路径

然后将资源啊包导入即可!资源包和工程在文章末尾都会提供!

简单看一下工程中的资源
模型资源

动画资源

图片素材

声音资源

差不多这些东西就够我们做一个Demo使用了!


🏳️‍🌈第二步:游戏场景搭建

  • 那现在一些基本的模型图片等素材有了,接下来就是需要一个游戏场景了👈!
  • 那是不是要我们自己搭建一个呢😬~
  • 正常的话是这样没错,但是我这么懒又没有时间,怎么会自己去费工夫搭建场景呢哈哈🤣!
  • 所以我们这里直接拿一个现成的场景来搞一下😝
  • 因为毕竟说到底搭建场景就是一个苦力活,对自身学习也没有太大的帮助😲~

一起来看一下这个场景吧!


🏳️‍🌈第三步:模型动画设置

  • 场景已经有了,那现在就开始给模型配置动画
  • 因为动画片段都是资源包中有了的,所以我们这里只需要创建动画控制器Animator Controller就好啦
  • 看到这有的小伙伴就要说了,啥都有了,那你做的也太简单了吧!!!
  • 害,这都是多亏了前辈们将框架结构搭建出来了。
  • 我们能做的就是站在巨人的肩膀上,做一些微不足道的打工仔的搬砖活罢了,一切为了学习和生活!

废话不多说,先来右键创建一个Animator Controller,如下图

敌人动画设置

  • 因为主角的动画控制器配置略微有些麻烦,因为主角可执行的动画确实太多了
  • 所以我们这里先从敌人的动画控制器配置开始
  • 创建完Animator Controller以后双击打开这个动画控制器

一开始是这样的,只有这三个动画状态

敌人的动画片段资源路径为:Enemy - > Animations文件夹下

我们先把待机状态Idle加进去当做默认状态,默认是黄色的,直接把动画片段拖到Animator窗口即可

然后将敌人的动画片段都拖上去,并简单的排个序!示例如下

  • 当前还只是将动画片段拖上去了,并没有进行逻辑关系设置

  • 那接下来先配置一下动画状态,只有先创建完动画状态

  • 在接下来的动画过度逻辑中才能正确的操作下去!

  • 来看一下设置的动画状态吧,下图所示

    其中,GroundHit、Death、Idel、Walk、KnockDown_Up、KnockDown_Down、KnockDown_End等等都是Trigger类型
    Blend和MovementSpeed都是Float类型的,isDead是Bool类型的

  • 为什么要这样设置不用的类型呢,这里再简单说一下

  • 因为攻击、死亡等状态都是需要执行的时候,执行一次就好,所以采用Trigger类型最合适!

  • 那移动速度这种自然用Float控制比较好,还可以控制

  • 最后还有一个isDead是否死亡肯定用Bool 值判断最好啦!简单的一个true和False表示是否死亡!

  • 然后给每个动画片段添加上动画过度参数,还不知道怎样添加的可以再去仔细看看我这篇动画基础教程哦

  • Unity零基础到进阶 ☀️| 近万字教程 对 Unity 中的 动画系统基础 全面解析+实战演练

给对应的动画过渡按照对应的动画状态添加上动画参数就好了!最终添加完的效果如下

主角动画设置
主角的动画片段相比较敌人的就会更多了一些,但是整体思路是一样的
先来看一下主角都有哪些动画片段

动画参数:

  • 具体动画控制器配置如下,看起来很复杂,其实就是一个动画之间过渡的参数配置
  • 这里就不挨个来说具体怎样配置的那些参数了,不知道咋配置的可以直接在源码工程里看一下就好啦!
  • 想明白思路之后其实就是一个体力活!

🏳️‍🌈第四步:模型基本移动跳跃等以及相机跟随等设置

基本的模型动画配置完了,那接下来就是角色移动的相关问题了
这里的话就要开始写代码了,一起来顺着思路看一下吧!

  • 这里的话我们采用两种移动方式
  • 一种是使用键盘控制角色移动,另一种则是通过虚拟按键来进行移动
  • 下面来看一下挂在主角身上的脚本PlayerMovement
  • 该脚本主要是控制主角的移动跳跃等功能的逻辑编写

先来看一下代码

	[Header("Settings")]
	public float walkSpeed = 3f;
	public float ZSpeed = 1.5f;
	public float JumpForce = 8f;
	public bool AllowDepthJumping;
	public float AirAcceleration = 3f;
	public float AirMaxSpeed = 3f;
	public float rotationSpeed = 15f;
	public float jumpRotationSpeed = 30f;
	public float lookAheadDistance = .2f;
	public float landRecoveryTime = .1f;
	public float landTime = 0;
	public LayerMask CollisionLayer;

这里提供了一些基本的设置信息,可以在unity面板上看到

	private List<UNITSTATE> MovementStates = new List<UNITSTATE> {
		UNITSTATE.IDLE,
		UNITSTATE.WALK,
		UNITSTATE.JUMPING,
		UNITSTATE.JUMPKICK,
		UNITSTATE.LAND,
	};

然后定义了一个列表存储了动画的几种状态,其中这个UNITSTATE是在另一个脚本中定义的属性

UnitState脚本内容如下,该脚本主要是用来管控角色的动画状态

using UnityEngine;

public class UnitState : MonoBehaviour {

	public UNITSTATE currentState = UNITSTATE.IDLE;

	public void SetState(UNITSTATE state){
		currentState = state;
	}
}

public enum UNITSTATE {
	IDLE,
	WALK,
	JUMPING,
	LAND,
	JUMPKICK,
	PUNCH,
	KICK,
	ATTACK,
	DEFEND,
	HIT,
	DEATH,
	THROW,
	PICKUPITEM,
	KNOCKDOWN,
	KNOCKDOWNGROUNDED,
	GROUNDPUNCH,
	GROUNDKICK,
	GROUNDHIT,
	STANDUP,
	USEWEAPON,
};
  • 然后还需要定义一个脚本用来控制Input输入
  • 那就来创建一个新脚本InputManager
  • 这里面是实际的Input输入方式控制
  • 加入了键盘输入控制方式自定义
	[Header("Keyboard keys")]
	public KeyCode Left = KeyCode.LeftArrow;
	public KeyCode Right = KeyCode.RightArrow;
	public KeyCode Up = KeyCode.UpArrow;
	public KeyCode Down = KeyCode.DownArrow;
	public KeyCode PunchKey = KeyCode.Z;
	public KeyCode KickKey = KeyCode.X;
	public KeyCode DefendKey = KeyCode.C;
	public KeyCode JumpKey = KeyCode.Space;

	[Header("Joypad keys")]
	public KeyCode JoypadPunch = KeyCode.JoystickButton2;
	public KeyCode JoypadKick = KeyCode.JoystickButton3;
	public KeyCode JoypadDefend = KeyCode.JoystickButton1;
	public KeyCode JoypadJump = KeyCode.JoystickButton0;

具体代码如下:

//delegates
	public delegate void InputEventHandler(Vector2 dir);
	public static event InputEventHandler onInputEvent;
	public delegate void CombatInputEventHandler(INPUTACTION action);
	public static event CombatInputEventHandler onCombatInputEvent;

	[Space(15)]
	public UIManager _UIManager; //link to the UI manager
	[HideInInspector]
	public Vector2 dir;
	private bool TouchScreenActive;
	public static bool defendKeyDown;

	void Start(){
		_UIManager = GameObject.FindObjectOfType<UIManager>();

		//automatically enable touch controls on ios or android
		#if UNITY_IOS || UNITY_ANDROID
			UseTouchScreenInput = true;
			UseKeyboardInput = UseJoypadInput = false;
		#endif
	}

	public static void InputEvent(Vector2 dir){
		if( onInputEvent != null) onInputEvent(dir);
	}

	public static void CombatInputEvent(INPUTACTION action){
		if(onCombatInputEvent != null) onCombatInputEvent(action);
	}

	public static void OnDefendButtonPress(bool state){
		defendKeyDown = state;
	}
		
	void Update(){

		//use keyboard
		if (UseKeyboardInput) KeyboardControls();

		//use joypad
		if (UseJoypadInput) JoyPadControls();

		//use touchScreen
		EnableDisableTouchScrn(UseTouchScreenInput);
	}

	void KeyboardControls(){
		
		//vector
		float x = 0f;
	 	float y = 0f;

		if (Input.GetKey (Left)) x = -1f;
		if (Input.GetKey (Right))x = 1f;
		if (Input.GetKey (Up)) y = 1f;
		if (Input.GetKey (Down)) y = -1f;
	
		dir = new Vector2(x,y);
		InputEvent(dir);

		//Combat input
		if(Input.GetKeyDown(PunchKey)){
			CombatInputEvent(INPUTACTION.PUNCH);
		}

		if(Input.GetKeyDown(KickKey)){
			CombatInputEvent(INPUTACTION.KICK);
		}
			
		if(Input.GetKeyDown(JumpKey)){
			CombatInputEvent(INPUTACTION.JUMP);
		}

		defendKeyDown = Input.GetKey(DefendKey);
	}

	void JoyPadControls(){
		float x = Input.GetAxis("Joypad Left-Right");
		float y = Input.GetAxis("Joypad Up-Down");

		dir = new Vector2(x,y);
		InputEvent(dir.normalized);

		if(Input.GetKeyDown(JoypadPunch)){
			CombatInputEvent(INPUTACTION.PUNCH);
		}

		if(Input.GetKeyDown(JoypadKick)){
			CombatInputEvent(INPUTACTION.KICK);
		}

		if(Input.GetKey(JoypadJump)){
			CombatInputEvent(INPUTACTION.JUMP);
		}

		defendKeyDown = Input.GetKey(JoypadDefend);
	}

	//enables or disables the touch screen interface
	public void EnableDisableTouchScrn(bool state){
		InputEvent(dir.normalized);

		if (_UIManager != null) {
			if (state) {
				
				//show touch screen
				if(!TouchScreenActive) {
					_UIManager.ShowMenu ("TouchScreenControls", false);
					TouchScreenActive = true;
				}

			} else {

				//hide touch screen
				if (TouchScreenActive) {
					TouchScreenActive = false;
					_UIManager.CloseMenu ("TouchScreenControls");
				}
			}
		}
	}

	//returns true if the defend key is held down
	public bool isDefendKeyDown(){
		return defendKeyDown;
	}
}

public enum INPUTACTION {
	NONE,
	PUNCH,
	KICK,
	JUMP,
	DEFEND,
	WEAPONATTACK,
}

public enum INPUTTYPE {
	KEYBOARD = 0,	
	JOYPAD = 5,	
	TOUCHSCREEN = 10, 
}
  • 这个脚本中不止自定义了几种键盘输入方式
  • 而且包括具体的键盘输入的方法调用和虚拟键盘输入方法的调用
  • 定义了几个枚举,说明了一下不同输入要执行的输出效果!
  • 还可以自由选择键盘输入或者虚拟按钮输入!
    [Header("使用键盘输入")]
    public bool UseKeyboardInput;
    [Header("使用手柄输入")]
    public bool UseJoypadInput;
    [Header("使用虚拟按键输入")]
    public bool UseTouchScreenInput;

相机跟随
给相机写一个脚本,挂载到摄像机上即可!
通过控制摄像机的偏移量,让相机一直保持正面对着角色~

using UnityEngine;

public class CameraFollow : MonoBehaviour {

	public Transform target;
	[Header ("Follow Settings")]
	public float distanceToTarget = 5; // The distance to the target
	public float heightOffset = 5; // the height offset of the camera relative to it's target
	public float viewAngle = 10; //a downwards rotation
	public Vector3 AdditionalOffset; //any additional offset
	public bool FollowZAxis; //enable or disable the camera following the z axis

	[Header ("Damp Settings")]
	public float DampX = 3f;
	public float DampY = 3f;
	public float DampZ = 3f;

	[Header ("View Area")]
	public float MinLeft;
	public float MaxRight;

	[Header ("Wave Area collider")]
	public bool UseWaveAreaCollider;
	public BoxCollider CurrentAreaCollider;
	public float AreaColliderViewOffset;

	void Start(){

		//set player as follow target
		if (!target) SetPlayerAsTarget();

		//set camera start position
		if (target) {
			Vector3 playerPos = target.transform.position;
			transform.position = new Vector3(playerPos.x, playerPos.y - heightOffset, playerPos.z + (distanceToTarget));
		}
	}

	void Update () {
		if (target){

			//initial values
			float currentX = transform.position.x;
			float currentY = transform.position.y;
			float currentZ = transform.position.z;
			Vector3 playerPos = target.transform.position;

			//Damp X
			currentX = Mathf.Lerp(currentX, playerPos.x, DampX * Time.deltaTime);

			//DampY
			currentY = Mathf.Lerp(currentY, playerPos.y - heightOffset, DampY * Time.deltaTime);

			//DampZ
			if (FollowZAxis) { 
				currentZ = Mathf.Lerp (currentZ, playerPos.z + distanceToTarget, DampZ * Time.deltaTime);
			} else {
				currentZ = distanceToTarget;
			}

			//Set cam position
			if(CurrentAreaCollider == null) UseWaveAreaCollider = false;
			if (!UseWaveAreaCollider) {
				transform.position = new Vector3 (Mathf.Clamp (currentX, MaxRight, MinLeft), currentY, currentZ) + AdditionalOffset;
			} else {
				transform.position = new Vector3 (Mathf.Clamp (currentX, CurrentAreaCollider.transform.position.x + AreaColliderViewOffset, MinLeft), currentY, currentZ) + AdditionalOffset;
			}

			//Set cam rotation
			transform.rotation = new Quaternion(0,180f,viewAngle,0);
		}
	}

	void SetPlayerAsTarget(){
		GameObject player =以上是关于一款类似“恐龙快打”的 横版街机格斗游戏 该如何制作?| 一起来学习 顺便送源码码文不易,建议收藏学习的主要内容,如果未能解决你的问题,请参考以下文章

恐龙快打无限子弹修改方案

恐龙快打无限子弹修改方案

《暗影格斗3》中的渲染优化

Unity应用《暗影格斗3》中的渲染优化!

教AI逐帧搓招玩《铁拳》通关最高难度,现在的街机游戏爱好者有点东西啊

用Python玩任何一款街机,带你一起回味童年!