【Unity】第三人称射击游戏开发过程之开火设计(TPS.S.P2)

【Unity】第三人称射击游戏开发过程之开火设计(TPS.S.P2)


这一章我们来设计基础的枪械功能。

枪械的开火

我现在选择的武器是416,所以数据会是416的一些数据。

开火流程

第一步,要找到枪口。我们之前已经做了瞄准点了,现在只需要让子弹从枪口出现,然后飞向瞄准点。
自然,这个射击逻辑由各位的想法而定。无论是射线射击,或者说实体子弹;无论是从瞄准镜飞出,还是从枪口飞出;无论是以瞄准点为方向,还是以枪口实际朝向作为飞行方向都可以。

开火前的准备

由于我们是从枪口生成实体子弹,所以需要准备子弹实体以及枪口位置。

实体子弹的准备

首先,我们的子弹虽然是实体的,但我们不需要其有模型。我们只希望由可视化的弹道。所以我们的子弹设计是这样子的:
1.由碰撞体与刚体以保证能与其他物体发生碰撞;
2.有弹道。
我们这里主要聊一下弹道。在Unity中有一个TrailRenderer(拖尾渲染器)。我们为子弹添加上这个组件。
这个红色曲线代表我们的拖尾在生存周期中的宽度变化。下边还有诸如颜色的变化,生存时间,材质等。
在这里插入图片描述
我们的拖尾在编辑器中拉动一下发现是粉红色的,这代表其未添加材质。我们为其创建一个。创建一个材质,Shader选择Legacy Shaders/Particles/Additive,这个材质代表了其属性是粒子效果。设置一下颜色
在这里插入图片描述
好,接下来为拖尾添加上这个材质
在这里插入图片描述
我们把它做成一个预制体。枪口就是简单的设置一个空物体挂载在枪口就可以了。

开火逻辑

这段代码我挂在了枪上。
我们首先获取属性

	[Header("Firing")]
    [Tooltip("是否为全自动")]
    [SerializeField]
    private bool automatic;
    [Tooltip("子弹飞行速度")]
    [SerializeField]
    private float _ammoSpeed;

    [Header("BaseSettings")]
    [Tooltip("枪口")]
    [SerializeField]
    private Transform muzzle;
    [Tooltip("瞄准点")]
    [SerializeField]
    private Transform targetpoint;
    [Tooltip("子弹")]
    [SerializeField]
    private GameObject _ammo;

    private PlayerInputsMassage _inputMessage;
    [Tooltip("子弹的对象池")]
    private ObjectPool<GameObject> ammoPool;

这里我考虑使用对象池来管理子弹。
对象池目前仍然有很多问题,大家可以使用普通的方法处理。对象池中会出现的问题是弹道显示,这个问题我没有找到解决方法之前,我并不推荐大家使用对象池
那么在Start中建立对象池。在建立对象池前,要先为子弹设计一个类去做碰撞相关的事情。
在这里我们设计了一个事件作为碰撞的事件。然后在Gun中就可以添加相应的方法让子弹执行。

public UnityEvent destroyEvent = new UnityEvent();

    public bool isDestroy;
    private void OnEnable()
    {
        isDestroy = false;
    }
    private void OnTriggerEnter(Collider other)
    {
        if (!other.CompareTag("Gun") && !isDestroy)
        {
            isDestroy = true;
            destroyEvent?.Invoke();
        }
    }
		_inputMessage = FindObjectOfType<PlayerInputsMassage>();
        ammoPool = new ObjectPool<GameObject>(
            () =>
            {
                var ammo = Instantiate(_ammo, muzzle);
                ammo.AddComponent<AmmoBehavier>().destroyEvent.AddListener(() =>
                {
                    ammoPool.Release(ammo);
                }
                );
                return ammo;
            },
            (go) =>
            {
                go.SetActive(true);
                go.transform.position = muzzle.position;
            },
            (go) =>
            {
                go.SetActive(false);
            },
            (go) => { Destroy(go); }
            );

接下来我们写一个Fire方法。
这里我们用对象池的get代替了平时的生成。生成则是交由对象池处理。

public void Fire()
    {
        Quaternion rotation = Quaternion.LookRotation(targetpoint.position - muzzle.position);
        GameObject ammo = ammoPool.Get();
        ammo.transform.rotation = rotation;
        ammo.GetComponent<Rigidbody>().velocity = ammo.transform.forward * _ammoSpeed;
    }

接下来我们设置一个非常好用的东西:EventHandler
可以认为这是一个专门处理事件的类,我们会将许多跨脚本的代码用它来写,会很方便调用。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public static class EventHandler
{
    public static event Action WeaponFire;
    public static void CallWeaponFire()
    {
        WeaponFire?.Invoke();
    }
}

这样开火的事件就写好了,我们只需要把每个代码需要处理与开火相关的内容写成一个方法,然后添加到这个事件中,然后由开火键触发这个方法即可。我们接下来简单演示一下(注意,这里我们的开火方法与触发都写在了Gun中,但以后涉及到枪械切换,就会将其分离)

private void OnEnable()
    {
        EventHandler.WeaponFire += Fire;//在该脚本启用时添加该方法
    }

    private void OnDisable()
    {
        EventHandler.WeaponFire -= Fire;//在该脚本停用时撤销该方法
    }

这样就做到了方法的注册,接下来是调用,该调用是可以放在任何地方,我只是希望将人物的移动与射击分离才这样写的。否则人物的控制脚本将会十分臃肿。

private void Update()
    {
        if(_inputMessage.fire&&_inputMessage.aim)
        {
            EventHandler.CallWeaponFire();
        }
    }

这一章的内容暂时先到这里,由于有些问题仍然没有得到解决,这一章可能后续修改的可能性比较大。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值