纪念下我的第一款游戏——《剑盾勇者》
生活随笔
收集整理的這篇文章主要介紹了
纪念下我的第一款游戏——《剑盾勇者》
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這篇博客記錄一下今年寒假里和幾位學長一起做的一款游戲,當時做這款游戲的目的是想著看看自己的水平,了解一下遠程辦公協同工作,順便參加一下比賽。然而事實證明,我還是太菜了😂。至于這款有點不好形容的游戲,大概就是我自己做的第一款游戲了。
需要說明一下的是做這個項目是不具備商業目的的,所有的模型以及部分腳本來自官方以及各種資源網站
源代碼可以在這里找到👉ShieldAndSword-Source-Code
-
概況
在這個項目中,我主要負責的是玩家的控制器和腳本,人形敵人的控制器和腳本,其中包括:
-
玩家的控制(主要是移動和攻擊用Animator Controller)
-
Cinemachine(攝像機)
-
玩家的攻擊對敵人的傷害(動畫事件)
-
玩家的受到傷害(血量、動畫、死亡)
-
人形敵人的追擊(包括了移動和Nav Mesh)
-
人形敵人的攻擊
-
人形敵人受到傷害(同玩家)
這是我負責的部分的工作截圖?
上面這個是游戲內截圖,完整項目的?
-
玩家角色的控制
我能用到的模型是一些lowpoly的模型,我們選了這個作為主角。這個模型自帶一些還過得去的移動,攻擊動畫。于是我自己搭了一個動畫狀態機,查考官方有一個項目的腳本寫了一個合適的。
動畫狀態機?
移動動畫的混合樹?
我的動畫機也就是能讓主角動一動,發出兩種形式的攻擊,受到傷害和倒地,其中Grounded是移動的混合樹。
移動的動畫通過混合樹實現,同時移動這件事本身也是通過動畫自帶的位移實現的。。。
移動方面的腳本的任務基本上就是獲取玩家的輸入并更新相應的動畫狀態,隨便弄幾段代碼吧。
/// <summary>/// 供調用的角色運動主方法/// </summary>/// <param name="move"></param>/// <param name="crouch"></param>/// <param name="jump"></param>public void Move(Vector3 move, bool crouch, bool jump){// convert the world relative moveInput vector into a local-relative// turn amount and forward amount required to head in the desired// direction.if (move.magnitude > 1f) move.Normalize();move = transform.InverseTransformDirection(move);CheckGroundStatus();move = Vector3.ProjectOnPlane(move, m_GroundNormal);m_TurnAmount = Mathf.Atan2(move.x, move.z);m_ForwardAmount = move.z;ApplyExtraTurnRotation();// control and velocity handling is different when grounded and airborne:if (m_IsGrounded){HandleGroundedMovement(crouch, jump);}else{HandleAirborneMovement();}ScaleCapsuleForCrouching(crouch);PreventStandingInLowHeadroom();// send input and other state parameters to the animatorUpdateAnimator(move);} void UpdateAnimator(Vector3 move){// update the animator parametersm_Animator.SetFloat("Forward", m_ForwardAmount, 0.1f, Time.deltaTime);m_Animator.SetFloat("Turn", m_TurnAmount, 0.1f, Time.deltaTime);//m_Animator.SetBool("Crouch", m_Crouching);//m_Animator.SetBool("OnGround", m_IsGrounded);if (!m_IsGrounded){m_Animator.SetFloat("Jump", m_Rigidbody.velocity.y);}// calculate which leg is behind, so as to leave that leg trailing in the jump animation// (This code is reliant on the specific run cycle offset in our animations,// and assumes one leg passes the other at the normalized clip times of 0.0 and 0.5)float runCycle =Mathf.Repeat(m_Animator.GetCurrentAnimatorStateInfo(0).normalizedTime + m_RunCycleLegOffset, 1);float jumpLeg = (runCycle < k_Half ? 1 : -1) * m_ForwardAmount;if (m_IsGrounded){m_Animator.SetFloat("JumpLeg", jumpLeg);}// the anim speed multiplier allows the overall speed of walking/running to be tweaked in the inspector,// which affects the movement speed because of the root motion.if (m_IsGrounded && move.magnitude > 0){m_Animator.speed = m_AnimSpeedMultiplier;}else{// don't use that while airbornem_Animator.speed = 1;}}這樣一來我們操控的角色能跑能跳能做個揮劍的動作了。
-
攝像機
我使用了官方的Cinemachine插件,在網上翻了一篇教程,隨便做了做.值得一提的是,因為角色在移動時頭會晃動,所以沒有讓攝像機對準頭而是專門做了一個點用來讓攝像機對準
-
角色的攻擊
我們已經有了攻擊的動畫,現在就是要讓我的動畫能真正對敵人產生傷害。既然如此,肯定要有一個敵人。
在做出完整的敵人之前,我先做了一個膠囊作為測試,這個膠囊添加了碰撞器和一個EnemyHealth腳本
public class EnemyHealth : MonoBehaviour {public float health = 15f;public Animator animator;public HumanoidEnemyController controller;public void TakeDamage(float amount){health -= amount;if(health <= 0){animator.SetBool("Die", true);controller.enabled = false;GameObject.Destroy(this.gameObject, 3f);//Debug.Log("Enemy dead!");}else{animator.SetTrigger("GetHit");}//Debug.Log("Enemy take some damage!");} }這個腳本也蠻簡單,引用敵人的動畫控制器(將來要用),定義血量,定義一個受到攻擊的函數,函數負責扣血,判斷死活,播放相應的動畫。函數將在玩家的攻擊腳本中被調用,所以是public的。
接著制作玩家的攻擊腳本,我的想法是給劍綁上觸發器,觸發器一碰到tag為“Enemy”的碰撞器就會觸發該敵人的扣血函數,同時為了讓主角的劍僅僅在揮出時才能造成傷害(不然敵人挨到就會扣血),我將觸發器在沒有揮出時(播放攻擊動畫時)關閉了,只有揮出時才打開。值得說一下的是,早期版本我沒有使用動畫事件,而是每次按下攻擊時打開主角劍的觸發器,然后用一個計時器到時去關閉劍的觸發器,這樣做其實問題也是有的。后來就改成了用攻擊動畫的動畫事件來進行劍的觸發器的開關。
只有在攻擊的一瞬間劍上的觸發器才是打開的?
下面兩個方法就是動畫事件要調用的觸發器開關👇
void EnableCollider(){weaponCol.enabled = true;}void DisableCollider(){weaponCol.enabled = false;}最后在劍上掛上腳本Weapon,這個腳本負責檢測碰撞并調用敵人的TakeDamage
public class Weapon : MonoBehaviour {public float damagePoint;void OnTriggerEnter(Collider other){if(other.tag == "Enemy"){//Debug.Log("Hit!");EnemyHealth e = other.GetComponent<EnemyHealth>();e.TakeDamage(damagePoint);}} }-
敵人的追擊
在參考了官方的3D game kit以后,我是這樣設計的
- 使用navmesh來尋路
- 敵人固定站在一個原點
- 玩家到達以敵人為中心的一個圓形范圍以后敵人開始追擊
- 玩家跑出范圍敵人馬上就不追了,回到原點
- 敵人離玩家的距離小于一定值后停止移動并開始攻擊(不停止移動的話會一直往主角身上撞)
- 攻擊時轉向玩家的最后位置(不能一直粘著玩家砍)
- 玩家死亡以后回到原點
超級沒品位的設計
腳本的公有成員變量?
部分代碼👇
public float lookRadius = 10f;//怪物探測范圍public float followRadius = 20f;//怪物活動范圍public GameObject player; //拖入player模型public GameObject model; // 拖入怪物模型 public CapsuleCollider Body; // 碰撞器public PlayerHealth playerHealth;public BoxCollider collider;public GameObject BasePos; // 怪物初始點public Vector3 NowPosition; //怪物現在的位置public Vector3 OriginPosition;//怪物初始位置public float distance_look;public float distance_follow;public float stopDistance;private Animator anim; Transform target;//目標NavMeshAgent agent;bool isAttacking;bool faceTarget; // Update is called once per framevoid Update(){NowPosition = transform.position;float distance_follow= Vector3.Distance(target.position,OriginPosition);//player與怪物初始位置的距離float distance_look = Vector3.Distance(target.position, transform.position);//怪物與player的距離anim.SetFloat("Attack", distance_look);//Debug.Log(distance_look);if (distance_look <= lookRadius && distance_follow <= followRadius){anim.SetBool("InPursuit", true);agent.SetDestination(target.position);// 追蹤目標Debug.Log(isAttacking);if (distance_look <= stopDistance || isAttacking){if(faceTarget)FaceTarget();agent.SetDestination(transform.position);}}else{anim.SetBool("InPursuit", false);agent.SetDestination(OriginPosition);//怪物回到初始位置 Vector3 toBase = OriginPosition - transform.position;toBase.y = 0;anim.SetBool("NearBase", toBase.sqrMagnitude < 2 * 2f);}if(playerHealth.isDead){agent.SetDestination(OriginPosition);//怪物回到初始位置 anim.SetFloat("Attack", 10f);}}-
敵人的攻擊
先設計了兩種人形敵人,BOSS和小怪
- 小怪:血薄,攻速快,使用的攻擊動畫為主角的輕砍
- BOSS:血更厚,攻擊速度更慢,使用的攻擊動畫為主角的二連擊
至于為什么要用主角的攻擊動畫,因為我沒動畫可用了(懶死)
攻擊的原理和主角是一樣的,一旦開始攻擊,播放攻擊動畫,將武器的觸發器打開。發起攻擊的時機為當敵人距離角色的距離小于一定值時。沒有設計攻擊間隔和其他招式。
正式的人形敵人分兩種,小怪和BOSS?(左BOSS,右小怪,不說你也看得出來)
-
玩家和敵人的受到傷害
在受到傷害時,根據動畫機的設計,會立即停下當前的動作(有時產生一個BUG),腳本會處理扣血并判斷是否已經死亡,如果沒有死亡,播放受到攻擊相應的動畫,死亡則播放死亡動畫并在動畫播完后刪除對象。(死亡動畫的播放也有BUG)
if (health <= 0){animator.SetBool("Die", true);GameObject.Destroy(this.gameObject, 3f);//Debug.Log("Player dead!");isDead = true;}-
不足之處
太多了,我覺得我可以列個表
-
主角的盾沒有用
本來想學黑魂來個盾可以格擋和盾反的,結果模型不帶那樣的動畫,我也不知道網上找到的其他動畫該怎么運用進來。而且我沒做過也不知道怎么做。 -
主角不能鎖定敵人
做不來,而且主角的移動方式限制了這一點,這個游戲的移動只能向前后移動,轉彎靠的是轉身子,不能走側步。真的不知道不能走側步還怎么鎖定。 -
敵人的動畫有點問題
當敵人死亡時,我希望他倒地了就不要起來,而不是一直重復倒地這個動作,因為不知道原因最后也沒能解決。 -
在正式項目中主角走著走著就卡住了
在我測試的時候,地面完全是平的,在正式項目中地面是凹凸的,我們團隊的另一位大佬給地面一塊一塊的添加了碰撞器的,但是不知道為什么主角會卡住。 -
受到傷害的動畫播放有延遲
我不知道是動畫的問題還是因為我沒好好調整。 -
有些時候,主角的劍會出現BUG
主角的劍在播放攻擊動畫的時候觸發器會開啟,這時候在一定特殊的情況下(主角的攻擊突然中斷)主角的劍的觸發器將是一直打開的。這個問題也沒能得到解決。 -
太粗糙了
不想說
-
寫在后面
我做我的工作,我的隊友們也做了他們的。在這里就不一一舉出了,但是我做的僅僅是這個游戲的一部分而已,是所有人的努力將這款游戲完整的做出來了。
雖然最后只拿了校賽二等獎,也因為題材的原因沒有參加省賽(血虧),但是也終究值得我紀念一下。
-
隨便截了一些圖
總結
以上是生活随笔為你收集整理的纪念下我的第一款游戏——《剑盾勇者》的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vivo浏览器不兼容bootstrap
- 下一篇: 一个生动的例子让你理解Linux的She