角色移动 通过RigidBody组件,Capsule Collider组件角色添加碰撞器以刚体组件,通过Player Movement脚本挂载到人物的模型上移动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 using UnityEngine;public class PlayerMovement : MonoBehaviour { public float MoveSpeed = 6f ; private Rigidbody rb; private void Awake () { rb = GetComponent<Rigidbody>(); } private void FixedUpdate () { float x = Input.GetAxisRaw("Horizontal" ); float y = Input.GetAxisRaw("Vertical" ); Move(x,y); } void Move (float h,float v ) { Vector3 Movement = new Vector3(h, 0 , v); Movement = Movement.normalized * MoveSpeed * Time.deltaTime; rb.MovePosition(transform.position + Movement); } }
相机跟随 通过玩家最开始的时候记录相机与玩家之间的距离差,当玩家进行移动的时候,用玩家当前的这个位置加上距离差得到相机的位置
(物体和组件的关系) 物体通过GetComponent<>得到组件,组件可以挂载物体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 using System.Collections;using System.Collections.Generic;using UnityEngine;public class CraemaFollow : MonoBehaviour { private float Smoothing = 5f ; private GameObject player; private Vector3 offset; private void Awake () { player = GameObject.FindGameObjectWithTag("Player" ); } private void Start () { offset = transform.position - player.transform.position; } private void FixedUpdate () { transform.position = Vector3.Lerp(transform.position,offset + player.transform.position,Smoothing*Time.deltaTime); } }
首先创建空物体,采用FindGameObjectWithTag(“Player”)函数找到player组件,在计算相机当前的位置与玩家位置的差值,因为玩家在不断移动,位置不断变化,要保证相机跟随,所以相机的位置为 固定的差值 + 玩家移动每帧移动的位置,并且使用Vector3.lerp函数保证两者间存在延后的跟随;
角色旋转 在unity中创造一个空物体,将其拉伸与地面平行,并创建floor图层。通过玩家发射射线,检测地面是否为floor图层,得到玩家需要的朝向,最后进行旋转。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class PlayerMovement : MonoBehaviour { public float MoveSpeed = 6f ; private Rigidbody rb; private Animator anim; private void Awake () { rb = GetComponent<Rigidbody>(); anim = GetComponent<Animator>(); } private void FixedUpdate () { float x = Input.GetAxisRaw("Horizontal" ); float y = Input.GetAxisRaw("Vertical" ); Turning(); } void Turning () { Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition); int floorLayer = LayerMask.GetMask("floor" ); RaycastHit floorHit; bool isTouchfloor = Physics.Raycast(cameraRay,out floorHit, 100 ,floorLayer); if (isTouchfloor) { Vector3 v3 = floorHit.point - transform.position; v3.y = 0 ; Quaternion quaternion = Quaternion.LookRotation(v3); rb.MoveRotation(quaternion); } } }
玩家射击 想要玩家进行射击,首先需要获取玩家的开火键,在获取开火键后,调用shoot函数之后,实现shoot里面的函数功能,包括开枪音效以及开枪的粒子特效,以及开枪的射线(类似于瞄准),为了可玩性,子弹的发射需要存在间隔
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public class Playershooting : MonoBehaviour { float time = 0f ; float timeBetweenBullets = 0.15f ; private AudioSource gunAudio; private Light gunlight; private float effectLightTime = 0.2f ; private ParticleSystem gunParticl; private LineRenderer gunLine; private Ray shootRay; private RaycastHit shoothit; private int shootMask; private void Start () { gunAudio = GetComponent<AudioSource>(); gunlight = GetComponent<Light>(); gunLine = GetComponent<LineRenderer>(); gunParticl = GetComponent<ParticleSystem>(); shootMask = LayerMask.GetMask("Enemy" ); } void Update () { time += Time.deltaTime; if (Input.GetKeyDown(KeyCode.Mouse0) && time >= timeBetweenBullets) { shoot(); } if (time >= effectLightTime * timeBetweenBullets) { gunlight.enabled = false ; gunLine.enabled = false ; } } void shoot () { gunlight.enabled =true ; time = 0f ; gunLine.SetPosition(0 , transform.position); gunLine.enabled = true ; gunParticl.Play(); gunAudio.Play(); shootRay.origin = transform.position; shootRay.direction = transform.forward; if (Physics.Raycast(shootRay, out shoothit, 100 , shootMask)) { gunLine.SetPosition(1 ,shoothit.point); MyenemyHealth enemyHealth = shoothit.collider.GetComponent<MyenemyHealth>(); enemyHealth.TakeDamage(10 ,shoothit.point); } else { gunLine.SetPosition(1 , transform.position+transform.forward*100 ); } } }
添加AI敌人(自动寻找player) 通过导入AI Navigation这个资源包,是生成的敌人具备自动导航的功能.定义了一个名为 Myenemy 的类,它继承自 MonoBehaviour,这意味着它可以作为一个脚本挂载到 Unity 游戏中的游戏对象上,用于控制敌人的行为。其主要功能是让敌人对象通过 NavMeshAgent 组件实现导航并跟随玩家,同时会根据敌人自身和玩家的存活状态来决定是否继续跟随。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Myenemy : MonoBehaviour { private NavMeshAgent nav; private GameObject player; private MyenemyHealth myEnemyhealth; private MyPlayerHealth myPlayerhealth; private void Awake () { player = GameObject.FindGameObjectWithTag("Player" ); nav = GetComponent<NavMeshAgent>(); myEnemyhealth = GetComponent<MyenemyHealth>(); myPlayerhealth = player.GetComponent<MyPlayerHealth>(); } void Update () { if (!myEnemyhealth.isDead&&!myPlayerhealth.isPlayerDead) nav.SetDestination(player.transform.position); else nav.enabled = false ; } }
攻击敌人 管理游戏中敌人的生命值、受伤和死亡逻辑。敌人可以受到伤害,当生命值降为 0 时会死亡,死亡后会播放死亡动画、音效,同时会给玩家增加分数,最后尸体逐渐消失。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.AI;public class MyenemyHealth : MonoBehaviour { private int EnemyScores = 10 ; private AudioSource audi; public AudioClip AudioClip; private Animator anim; private ParticleSystem enemyParticle; public int StartHealth = 100 ; private CapsuleCollider enemyCapsuleCollider; public bool isDead = false ; private bool isSiking = false ; private void Awake () { audi = GetComponent<AudioSource>(); enemyParticle = GetComponentInChildren<ParticleSystem>(); anim = GetComponent<Animator>(); enemyCapsuleCollider = GetComponentInChildren<CapsuleCollider>(); } void Update () { if (isSiking) { transform.Translate(-transform.up * Time.deltaTime*5f ); } } public void TakeDamage (int amount,Vector3 hitPoint ) { if (isDead==true ) return ; audi.Play(); StartHealth -= amount; enemyParticle.transform.position = hitPoint; enemyParticle.Play(); if (StartHealth <= 0 ) { Death(); } } public void Death () { isDead = true ; anim.SetTrigger("Death" ); enemyCapsuleCollider.enabled = false ; GetComponent<NavMeshAgent>().enabled = false ; GetComponent<Rigidbody>().isKinematic = true ; audi.clip = AudioClip; audi.Play(); MyPlayerScore.Scores += EnemyScores; } public void StartSinking () { isSiking = true ; Destroy(gameObject,2f ); } }
玩家血量 管理游戏中玩家的生命值、受伤和死亡逻辑。玩家受伤时屏幕会闪烁红色,生命值降为 0 时玩家会死亡,播放死亡动画和音效,同时禁止玩家移动和射击,还提供了重新加载关卡的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.SceneManagement;using UnityEngine.UI;public class MyPlayerHealth : MonoBehaviour { public Text PlayerHealthUi; public Image DamageImage; private bool Isdamaged = false ; public Color FlashColor = new Color (1f ,0f ,0f ,1f ); public AudioClip Ac_playerDeath; private Animator anim; private AudioSource Au_playerHurt; public int PlayerHealth = 100 ; public bool isPlayerDead = false ; private PlayerMovement playerMovement; private Playershooting playershooting; private void Awake () { Au_playerHurt = GetComponent<AudioSource>(); anim = GetComponent<Animator>(); playerMovement = GetComponent<PlayerMovement>(); playershooting = GetComponentInChildren<Playershooting>(); } void Update () { if (Isdamaged) { DamageImage.color = FlashColor; } else { DamageImage.color =Color.Lerp(DamageImage.color,Color.clear,5f *Time.deltaTime); } Isdamaged = false ; } public void TakeDamage (int PlayerHurt ) { Isdamaged = true ; if (isPlayerDead) return ; Au_playerHurt.Play(); PlayerHealth = PlayerHealth - PlayerHurt; if (PlayerHealth <= 0 ) { Death(); } PlayerHealthUi.text = PlayerHealth.ToString(); } void Death () { anim.SetTrigger("Dead" ); isPlayerDead = true ; Au_playerHurt.clip = Ac_playerDeath; Au_playerHurt.Play(); playerMovement.enabled = false ; playershooting.enabled = false ; } public void RestartLevel () { SceneManager.LoadScene(0 ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class MyEnemyAttack : MonoBehaviour { public int EnemyAttackDamage = 20; //enemy的攻击力 private MyPlayerHealth Myplayerhealth; private bool playerInRange; //判断玩家是否在enemy的触发器中 private GameObject player; private float Timer = 0;//敌人攻击的时间间隔 private Animator anim; private void Awake() { player = GameObject.FindGameObjectWithTag("Player"); Myplayerhealth = player.GetComponent<MyPlayerHealth>(); anim = GetComponent<Animator>(); } // Update is called once per frame void Update() { Timer += Time.deltaTime; if (playerInRange&&Timer >0.5f&&!Myplayerhealth.isPlayerDead) { //敌人如果离player很近造成伤害 Attack(); } if (Myplayerhealth.isPlayerDead)//如果player死亡(isPlayerDead = true),enemy静止不动 { anim.SetTrigger("PlayerDead");//播放enemy的静止动画 } } private void Attack() { Timer = 0; //获取玩家组件 Myplayerhealth.TakeDamage(EnemyAttackDamage); } private void OnTriggerEnter(Collider other)//enemy的敌人触发器(相当于攻击范围) { if (other.gameObject == player) { playerInRange = true; } } private void OnTriggerExit(Collider other) { if (other.gameObject == player) { playerInRange = false; } } }
敌人生成 游戏中定时生成敌人。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using System.Collections;using System.Collections.Generic;using UnityEngine;public class MyEnemyManager : MonoBehaviour { public GameObject Enemy; public float creatEnemyTime = 3f ; public GameObject CreatEnemyPoint; public float FirstEnemyTime = 1f ; void Start () { if (Enemy == null || CreatEnemyPoint == null ) { Debug.LogError("Enemy 或 CreatEnemyPoint 未赋值,请在 Unity 编辑器中进行设置。" ); return ; } InvokeRepeating("Spawn" , FirstEnemyTime, creatEnemyTime); } private void Spawn () { Instantiate(Enemy, CreatEnemyPoint.transform.position, CreatEnemyPoint.transform.rotation); } }