Unity makes SurvivalShooter game

Control Player movement:

public float speed;
private void FixedUpdate()
{
    float moveHorizontal = Input.GetAxis("Horizontal");/*水平方向的移动输入,左右*/
    float moveVertical = Input.GetAxis("Vertical");/*垂直方向上的移动输入,前后*/
    Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);/*movement中存放了各方向上移动的数据,是矢量*/
    movement = movement.normalized * speed ;/*规范化后movement长度由speed决定*/
    Rigidbody rigidbody = GetComponent<Rigidbody>();
    rigidbody.velocity = movement;
}

Note that when the Player hits an obstacle in the environment, the J8 will move randomly. In its Rigidbody, Freeze all the x, y, and z of the Rotation to solve the problem.
Control the Camera to follow the Player:

public GameObject player;
private Vector3 offset;
void Start () {
    offset = transform.position;
}   
void LateUpdate () {
    transform.position = offset + player.transform.position;
}

Player Animator (animation state machine) settings :
The Player in the Model has its own split animation, and some animation settings can be performed in its Inspector, but for when to perform which animation, and the switching relationship between different animations, you need to pass Create an Animator component, define related parameters, and control the association between different animations.
write picture description here
Drag each animation directly into the Animator, use transition to connect the connection, click on these connections, and create some parameters (Parameter) in the Inspector to control what kind of animation to switch under what circumstances, pay attention to the Has Exit time inside Do not tick.
Implementation code:

private void FixedUpdate()
{
    float moveHorizontal = Input.GetAxis("Horizontal");/*水平方向的移动输入,左右*/
    float moveVertical = Input.GetAxis("Vertical");/*垂直方向上的移动输入,前后*/
    Animator anim = GetComponent<Animator>();
    bool walking = moveHorizontal != 0 || moveVertical != 0;
    anim.SetBool("IsWalking", walking);
}

Zom Animator (animation state machine) settings :
For objects with the same model structure, the same Animator can be shared.
For objects with different actions but the same logical connection between different actions, you can create an Override of Animator, and only change the specific animation in it.
write picture description here
Control the Player to always face the mouse pointing :

int floorMask;
float camRayLength = 100F;/*经过主相机和鼠标位置的射线的极限长度*/
Rigidbody rigidbody;
void Awake () {
    floorMask = LayerMask.GetMask("Floor");
    rigidbody = GetComponent<Rigidbody>();/*在Awake中赋值一次,之后各方法中可直接调用rigidbody而无需赋值*/
}
private void FixedUpdate()
{
    Turning();
}
void Turning()
{
    Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);/*定义一条从mainCamera到鼠标的射线*/
    RaycastHit floorhit;/*存储碰撞点信息的变量*/
    if(Physics.Raycast(camRay, out floorhit, camRayLength, floorMask))/*圣典:当光线投射与任何碰撞器交叉时为真,否则为假。即射线碰撞到了某个碰撞器*/
    {
        Vector3 playerToMouse = floorhit.point - transform.position;/*计算出Player与碰撞点的相对位置信息*/
        playerToMouse.y = 0f;/*只是平行于floor的旋转*/
        Quaternion newRotation = Quaternion.LookRotation(playerToMouse);/*Quaternion为四元数*/
        rigidbody.MoveRotation(newRotation);
    }
}

Add NavMeshAgent to control the automatic navigation of Zom :
write picture description here
set Radius and Height, and completely include Object in the frame.
Open the Window->Navigation window,
write picture description here
select Environment (to check Static in its Inspector), click Bake in the window,
write picture description here
the part that can walk in the scene will be blue, the part that cannot walk will be purple, uncheck Show NavMesh, Colors are hidden.
Implementation code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;/*要定义NavMeshAgent变量,一定要有这一步*/
public class ZomMove : MonoBehaviour {
Transform player;
NavMeshAgent nav;
void Start () {
    player = GameObject.FindGameObjectWithTag("Player").transform;
    nav = GetComponent<NavMeshAgent>();
}
void Update () {
    nav.SetDestination(player.position);
}
}

Implement Zom attack and Player death
PlayerHealth:

public class PlayerHealth : MonoBehaviour {
public int startHealth = 100;
public int currentHealth;
PlayerMove playermove;
Animator anim;
bool isDead;/*是否死亡*/
bool damaged;/*是否受攻击*/
void Start () {
    playermove = GetComponent<PlayerMove>();/*在开头声明,在Start里初始化,如此在后面的所有方法中均可直接调用*/
    anim = GetComponent<Animator>();
    currentHealth = startHealth;
}
public void TakeDamage(int amount)
{
    damaged = true;
    //受伤后画面变红的代码
    currentHealth = currentHealth - amount;
    if (currentHealth <= 0 && !isDead)/*血量为零且没有运行过死亡函数*/
    {
        Death();
    }
}
void Death()
{
    isDead = true;
    //死亡后黑屏代码
    anim.SetTrigger("Dead");/*播放死亡动画*/
    playermove.enabled = false;/*禁用掉PlayerMove脚本,无法继续控制Player移动*/
}
}

ZomAttack:

public float timeBetweenAttacks=0.5f;/*攻击间隔*/
public int attackDamage=10;/*攻击伤害*/
Animator anim;/*当Player死亡后Zom由Move切换为Idle*/
GameObject player;/*攻击谁*/
PlayerHealth playerHealth;/*调用该脚本中的TakeDamage方法*/
ZomHealth zomHealth;
bool playerInRage;/*用来判断Player是否进入攻击范围*/
float timer;/*计时器*/
void Start () {
    //变量的初始化
    anim = GetComponent<Animator>();
    player = GameObject.FindGameObjectWithTag ("Player");
    playerHealth = player.GetComponent<PlayerHealth>();/*脚本也算作组件,语句最后的()不要忘*/
    zomHealth = GetComponent<ZomHealth>();
}
private void OnTriggerEnter(Collider other)/*进触发器*/
{
    if(other.gameObject==player)/*这的gameObject绝对不能丢*/
    {
        playerInRage = true;
    }
}
private void OnTriggerExit(Collider other)/*出触发器*/
{
    if (other .gameObject== player)/*因吹斯听*/
    {
        playerInRage = false;
    }
}
void Attack()
{
    timer = 0f;/*每次发起攻击计时器清零*/
    if (playerHealth.currentHealth > 0)
    {
        playerHealth.TakeDamage(attackDamage);
    }
}
void Update () {
    timer = timer + Time.deltaTime;/*从此刻开始计时*/
    if (playerInRage && timer >= timeBetweenAttacks)
    {
        Attack();
    }
    if (playerHealth.currentHealth <= 0)/*当Player死亡让Zom变为Idle状态*/
    {
        anim.SetTrigger("Dead");
    }
}

Use the Player's Death animation event to achieve its "resurrection":
write picture description here
it means that the function RestartLevel will be called at that point in time.
Code:

void RestartLevel()
{
    Application.LoadLevel(Application.loadedLevel);/*调入当前已经调用的场景*/
}

Implement Player Attack
write picture description here

public int damagePerShot = 20;
public float timeBetweenBullets = 0.15f;/*射击间隔*/
public float range = 100f;/*射击范围*/
float timer=0;/*计时器,控制子弹发射频率*/
Ray shootRay;/*射线*/
RaycastHit shootHit;/*保存射线碰撞的结果*/
int shootableMask;/*可射击区域,注意要将环境和Zoms的Layer设置为Shootable才行*/
ParticleSystem gunParticles;/*粒子效果*/
LineRenderer gunLine;
Light gunLight;
AudioSource gunAudio;
float effectsDisplayTime = 0.2f;
void Start () {
    shootableMask = LayerMask.GetMask("Shootable");
    gunParticles = GetComponent<ParticleSystem>();
    gunLine = GetComponent<LineRenderer>();
    gunLight = GetComponent<Light>();
    gunAudio=GetComponent<AudioSource>();
}
void Update () {
    timer = timer + Time.deltaTime;/*计时*/
    if (Input.GetButton("Fire1") && timer >= timeBetweenBullets && Time.timeScale != 0)
    {
        Shoot();
    }
    if (timer >= timeBetweenBullets * effectsDisplayTime)/*如果发射子弹,在一定时间后将所有特效禁用*/
    {
        DisableEffects();
    }
}
public void DisableEffects()
{
    gunLight.enabled = false;/*禁用光效*/
    gunLine.enabled = false;/*禁用线渲染器*/
}
void Shoot()
{
    timer = 0f;/*每一次发射子弹立即将计时器清零*/
    gunLight.enabled = true;/*默认为禁用状态,只有在调用Shoot函数时启用*/
    gunParticles.Stop();/*粒子处于何种状态也不清楚,所以直接先禁用再播放*/
    gunParticles.Play();
    gunAudio.Play();
    gunLine.enabled = true;
    gunLine.SetPosition(0, transform.position);/*设置线渲染器第一个端点的位置为枪口*/
    shootRay.origin = transform.position;/*设置射线发射点的位置*/
    shootRay.direction = transform.forward;/*设置射线方向为枪口方向(Z)由鼠标决定*/
    if(Physics.Raycast(shootRay,out shootHit, range, shootableMask))/*如果射线在range内碰撞到了什么东西*/
    {
        ZomHealth zomHealth = shootHit.collider.GetComponent<ZomHealth>();/*由碰撞信息得到Zom的脚本*/
        if (zomHealth != null)/*射线碰到的是Zom*/
        {
            zomHealth.TakeDamage(damagePerShot);
        }
        gunLine.SetPosition(1, shootHit.point);/*不管碰到的是谁,第二个端点必是碰撞点*/
    }
    else
        gunLine.SetPosition(1, shootRay.origin+shootRay.direction * range);/*在range内什么也没碰到*/
}
}

The specific settings of AudioSource are shown
write picture description here
in the figure. As the Player turns with the mouse before, the Z axis of GunBarrelEnd always points to the same direction and cannot point to the mouse direction in real time. It turns out that the use world space option is not checked in the LineRenderer.
write picture description here
Generate monster waves:

public GameObject Zom1;
public GameObject Zom2;
public GameObject Zom3;
public GameObject Position1;
public GameObject Position2;
public GameObject Position3;
public int ZomCount;
public float startWait=2f;
void Start()
{
    StartCoroutine(ZomWaves());/**协程调用*/
}
// Update is called once per frame
IEnumerator ZomWaves()
{/**函数要成为协同程序,必须返回IEnumerator,且在调用时要协程调用*/
    yield return new WaitForSeconds(startWait);/**暂停代码而不暂停游戏,这个方法只有在协同程序中可用*/
    for (int i = 0; i < ZomCount; i++)
    {
        Instantiate(Zom1, Position1.transform);
        Instantiate(Zom2, Position2.transform);
        Instantiate(Zom3, Position3.transform);
        yield return new WaitForSeconds(1f);
    }
}

The variable is declared in the script. If it is a component of this Object, it is initialized in Awake or Start. If it is another Object, it is directly dragged into the Inspector.

UI production

After the character is injured, the screen flashes red, with blood bars

write picture description here
This UI window refers to the Game window.
write picture description here
Implementation code:

public int startHealth = 100;
public int currentHealth;
PlayerMove playermove;
Animator anim;
bool isDead;/*是否死亡*/
bool damaged;/*是否受攻击*/
PlayerShoot playerShoot;
AudioSource playerDeath;
AudioSource playerHurt;
public Slider healthSlider;
public Image damage;
public float flashSpeed = 5f;
public Color flashColor = new Color(1f, 0f, 0f, 0.1f);/*初始化为红色*/
void Start () {
    playermove = GetComponent<PlayerMove>();/*在开头声明,在Start里初始化,如此在后面的所有方法中均可直接调用*/
    anim = GetComponent<Animator>();
    currentHealth = startHealth;
    playerShoot = GetComponentInChildren<PlayerShoot>();
    playerDeath = GetComponent<AudioSource>();
    playerHurt = GetComponent<AudioSource>();
}
private void Update()
{
    healthSlider.value = currentHealth;
    if(damaged)
    {
        damage.color = flashColor;/*如果受攻击使画面变红*/
    }
    else
    {
        damage.color = Color.Lerp(damage.color, Color.clear, flashSpeed * Time.deltaTime);/*如果没受攻击使画面恢复*/
    }
    damaged = false;/*damaged默认为假*/
}
public void TakeDamage(int amount)
{
    damaged = true;
    //受伤后画面变红的代码
    playerHurt.Play();
    currentHealth = currentHealth - amount;
    if (currentHealth <= 0 && !isDead)/*血量为零且没有运行过死亡函数*/
    {
        Death();
    }
}

play death animation

Create a new Imagine and Text, select Canvas, and create an animation in Window->Animation.
Then set the animation in AnimationController (Animator). Add a new script to the Canvas to control the parameters of the switching animation, the code is as follows:

Animator anim;
PlayerHealth playerHealth;
public GameObject player;/*别漏了这一步*/
void Start () {
    anim = GetComponent<Animator>();
    playerHealth = player.GetComponent<PlayerHealth>();/*通过此种方法可以直接引用其他脚本中的变量*/
}
void Update () {
    if(playerHealth.isDead)
    {
        anim.SetTrigger("GameOver");
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325968397&siteId=291194637