阶段学习总结-坦克大战案例

今天学习了一个案例-坦克大战,最终跟着视频做出了一个小demo,学到了许多实际操作,特记录下来,也是复习。

一.控制移动和旋转

public class TankMovement : MonoBehaviour
{
    //移动速度
    public float speed = 5;
    //旋转速度
    public float angularSpeed = 3;
    //刚体组件
    Rigidbody rigidbody;
    //坦克编号,这个脚本用于控制不止一个坦克,但是是从同一个prefab实例化而来,因此通过更改编号使不同按键能控制不同坦克
    public int number = 1;

    //闲置发动机音效
    public AudioClip idleAudio;
    //开动坦克发动机音效
    public AudioClip drivingAudio;

    private AudioSource audioSource;
    // Start is called before the first frame update
    void Start()
    {
        //获取刚体组件
        rigidbody = GetComponent<Rigidbody>();
        //获取声音源组件
        audioSource = GetComponent<AudioSource>();
    }

    // Update is called once per frame
    void Update()
    {
        //获取轴的值
        float h = Input.GetAxis("HorizontalPlayer" + number);
        float v = Input.GetAxis("VerticalPlayer" + number);
        //通过刚体组件给与速度和旋转
        rigidbody.velocity = (transform.forward * v + transform.right * h) * speed;
        rigidbody.angularVelocity = transform.up * h * angularSpeed;

        //声音控制,如果物体移动,播放移动发动机声音,否则播放闲置发动机音效
        if(Mathf.Abs(h) > 0.1 || Mathf.Abs(v) > 0.1)
        {
            audioSource.clip = drivingAudio;
            //通过声音源组件的isPlaying()方法判断发动机音效是否已经播放,不做判断的话每一帧都会重新播放音效
            if(audioSource.isPlaying == false)
                audioSource.Play();
        }
        else
        {
            audioSource.clip = idleAudio;
            if(audioSource.isPlaying == false)
                audioSource.Play();
        }
    }
}

这是一个通过刚体控制物体移动和旋转的脚本,同时能控制声音播放,注意在刚体组件中锁定y轴位置和xz轴旋转,这也是这段脚本的控制方式的缺陷,只能控制平面上物体的运动

坦克上的刚体组件,可以看到锁定了y轴位置和xz轴旋转

轴设置,分别添加了player1和player2的轴,可以通过wasd和方向键分别控制两个tank的移动

 

 二.发射炮弹

 同样的,这个脚本同时有控制声音的功能,发射炮弹分为实例化炮弹、赋予炮弹初速度两步,炮弹的方向和发射角度信息通过tank下的一个子物体的位置和方向记录下来

public class TankAttack : MonoBehaviour
{
    //炮弹实例
    public GameObject shellPrefab;
    //开火键,为不同的tank可以分别设置不同的开火键
    public KeyCode fireKey = KeyCode.Space;
    //炮弹速度
    public float shellSpeed = 500;
    //炮弹生成位置,这是一个在炮口的空物体,并设置好空物体的旋转,这个物体记录了炮弹的发射方向和角度信息,物体为tank的子物体,始终在炮口处
    private Transform firePosition;

    //发射音效
    public AudioClip shootAudio;
    // Start is called before the first frame update
    void Start()
    {
        //获取炮弹生成位置
        firePosition = transform.Find("FirePosition");
    }

    // Update is called once per frame
    void Update()
    {
        //当按下发射键时
        if (Input.GetKeyDown(fireKey))
        {
            //生成炮弹
            GameObject go = Instantiate(shellPrefab, firePosition.position, firePosition.rotation);
            //在tank上播放发射音效
            AudioSource.PlayClipAtPoint(shootAudio, transform.position);
            //通过炮弹上的刚体组件赋予炮弹初速度
            go.GetComponent<Rigidbody>().velocity = go.transform.forward * shellSpeed;
        }
    }
}

3.炮弹爆炸和销毁

public class Shell : MonoBehaviour
{
    //炮弹爆炸特效
    public GameObject shellExplosion;
    //炮弹爆炸音效
    public AudioClip shellExplosionAudio;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    //触发器,这里的碰撞检测采用触发检测
    private void OnTriggerEnter(Collider other)
    {
        //在碰撞位置实例化一个爆炸特效物体,直接播放爆炸特效
        Instantiate(shellExplosion, transform.position, transform.rotation);
        //播放爆炸音效
        AudioSource.PlayClipAtPoint(shellExplosionAudio, transform.position);
        //通过标签检测是否碰撞到的是tank,tank的prefab需要设置好标签
        if(other.tag == "Tank")
        {
            //发送消息给tank上的脚本中的方法,扣除血量
            other.SendMessage("TankDamage");
        }
        //销毁炮弹
        Destroy(this.gameObject);
    }
}

这里采用触发检测,避免了碰撞导致的tank有位移等物理效果,需要在炮弹的prefab的collision中将is trigger勾选

 脚本发送了扣血的消息给tank,因此tank上需要再添加扣血的脚本组件

public class TankHealth : MonoBehaviour
{
    //坦克血量
    public int hp = 100;
    //坦克爆炸预制件,血量扣完就会发生爆炸
    public GameObject tankExplosion;
    //爆炸音效
    public AudioClip tankExploreAudio;

    //总血量,用于计算坦克血量比例
    private int maxHP;
    //用于显示坦克血量的滑动条
    public Slider slider;
    // Start is called before the first frame update
    void Start()
    {
        //初始化最大血量
        maxHP = hp;
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    //扣血方法
    void TankDamage()
    {
        //中弹后随机扣除10点以内的血量
        hp -= Random.Range(0, 10);
        //设置滑动条的值,显示剩余血量占比
        slider.value = (float)hp / maxHP;
        //血量扣完
        if(hp <= 0)
        {
            //老三套,实例化爆炸预制件
            Instantiate(tankExplosion, transform.position + Vector3.up, transform.rotation);
            //播放音效
            AudioSource.PlayClipAtPoint(tankExploreAudio,transform.position);
            //销毁当前物体
            Destroy(this.gameObject);
        }
    }
}

这里又添加了一个滑动条来显示血量,滑动条及其所在的画布需要设置为tank的子物体以使得滑动条跟随坦克移动

这里我们发现,坦克和炮弹的爆炸特效物体生成后,播放完效果并未销毁,因此需要在它们的prefab上添加销毁的自制组件

public class DestroyForTime : MonoBehaviour
{
    //销毁时间,给与动画播放充足的时间
    public float time = 1.5f;
    // Start is called before the first frame update
    void Start()
    {
        //销毁物体
        Destroy(this.gameObject, time);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}
原文地址:https://www.cnblogs.com/movin2333/p/14045403.html