坦克项目的摄像机改进及殉爆效果开发记录——Unity随手记(2020.12.1)

好久不见。
今天来汇报一下进度,等这次任务完成所有的这些随手记都会被一个完整的博客代替。
由于时间有点久了,我就不按顺序来了。
首先要说的是我放弃了Cinemachine插件,到油管上学了一下Sebastian Lague大佬2016年发布的一个系列视频
Character Creation (E08: third person camera)
这个可以说是相当简单的第三人称摄像机脚本了。我自己拿到手添加了用滚轮拉近拉远的功能。大佬不愧是大佬,代码十分简洁,相比之下我写的简直就是垃圾。

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

public class ThirdPersonCamera : MonoBehaviour
{
    
    
    // 鼠标灵敏度
    public float mouseSensitivity = 10f;
    // 滚轮灵敏度
    public float mouseScrollSensitivity = 5f;
    // 第三人称目标
    public Transform target;
    // 摄像机离目标的位置
    public float distanceFromTarget = 2f;
    // y方向的限制
    public Vector2 pitchMinMax = new Vector2(-40f, 85f);
    // 旋转运动平滑时间
    public float rotationSmoothTime = 0.12f;
    // 是否锁定鼠标
    public bool lockCursor;

    // x方向
    private float yaw;
    // y方向
    private float pitch;
    // 滚轮拉近拉远摄像机
    private float distance;
    // 旋转运动平滑速度
    Vector3 rotationSmoothVelocity;
    // 当前的旋转
    Vector3 currentRotation;

    void Start()
    {
    
    
        if(lockCursor)
        {
    
    
            Cursor.lockState = CursorLockMode.Locked;
            Cursor.visible = false;
        }

        distance = distanceFromTarget;
    }

    // 使用LateUpdate在target.position设置好以后设置摄像机的位置
    void LateUpdate()
    {
    
    
        yaw += Input.GetAxis("Mouse X") * mouseSensitivity;
        pitch -= Input.GetAxis("Mouse Y") * mouseSensitivity;
        distance -= Input.GetAxis("Mouse ScrollWheel") * mouseScrollSensitivity;
        
        pitch = Mathf.Clamp(pitch, pitchMinMax.x, pitchMinMax.y);
        distance = Mathf.Clamp(distance, 3f, 16f);

        currentRotation = Vector3.SmoothDamp(currentRotation, new Vector3(pitch, yaw, 0f), ref rotationSmoothVelocity, rotationSmoothTime);

        Vector3 targetRotation = currentRotation;
        transform.eulerAngles = targetRotation;

        distanceFromTarget = distance;

        // 摄像机的位置设置在目标位置减去自身z轴方向上的特定距离
        transform.position = target.position - transform.forward * distanceFromTarget;
    }
}

至于为什么不用Cinemachine呢,一是到时候展示的时候怕老师的电脑没有Unity2019,二是确实用不上那么完善的套件。
然后就是还做了一个第一人称的摄像机,方便瞄准远距离的目标,可以按左shift键来切换第三人称摄像机和第一人称摄像机,第一人称摄像机可以通过滚轮来实现开镜的那种效果,原理就是改fov,同时还有一个小细节就是视野拉近以后鼠标灵敏度也要降低才行。以下是第一人称相机代码。

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

public class FPCamera : MonoBehaviour
{
    
    
    public Transform cameraTransform;
    public float mouseSentivity;
    public Vector2 maxMinAngle;
    public float mouseScrollSpeed = 5f;

    private Vector3 m_mouseInputValue;

    private void Start()
    {
    
    
        m_mouseInputValue = new Vector3();
    }

    void Update()
    {
    
    
        float mouseX = Input.GetAxis("Mouse X");
        float mouseY = Input.GetAxis("Mouse Y");
        float mouseScrollWheel = -Input.GetAxis("Mouse ScrollWheel");
        m_mouseInputValue.y += mouseX * mouseSentivity;
        m_mouseInputValue.x -= mouseY * mouseSentivity;
        
        // 限制垂直旋转的角度(以符合现实情况)
        m_mouseInputValue.x = Mathf.Clamp(m_mouseInputValue.x, maxMinAngle.x, maxMinAngle.y);
        // 水平旋转整个Controller
        this.transform.localRotation = Quaternion.Euler(0, m_mouseInputValue.y, 0);
        // 垂直只旋转摄像机
        cameraTransform.localRotation = Quaternion.Euler(m_mouseInputValue.x, 0, 0);
        // 摄像机缩放
        float fov = cameraTransform.GetComponent<Camera>().fieldOfView;
        mouseSentivity = 0.5f + (fov / 10);
        fov += mouseScrollWheel * mouseScrollSpeed;
        fov = Mathf.Clamp(fov, 5f, 40f);
        cameraTransform.GetComponent<Camera>().fieldOfView = fov;
    }
}

有一个小问题就是第一人称和第三人称摄像机各管各的,相互不联动,导致第一人称摄像机看着一个方向,切换回第三人称看的就是第三人称切换前的方向,这样会让人感觉很奇怪,我想什么时候给他整合一下。


然后就是再往前一段时间实现的殉爆和击毁功能。
在这里插入图片描述
办法简单粗暴,直接替换掉游戏对象为素材里面的击毁坦克的预制体。所谓殉爆就是制作一个车身的游戏对象,一个炮塔的游戏对象,将要殉爆的对象替换为这两个对象到正确的位置,然后给炮塔一个力,我试着用随机数让每一次殉爆的效果略有不同。
提到了击毁就不得不聊一下我构思的毁伤机制,其实很简单,打中了以后在
挂载到那个敌人身上的脚本里面用随机数来决定是击毁,成员昏迷/配件损坏,还是没有伤害。为啥不是计算装甲厚度然后吧啦吧啦吧啦呢?因为装甲啥的咱也不会做啊。

    private DamageType FirePowerTest()
    {
    
    
        DamageType damage = DamageType.NoEffect;
        float firePower = Random.Range(2f, 7f);
        if(firePower < armor)
        {
    
    
            damage = DamageType.NoEffect;
        }
        else
        {
    
    
            if(firePower < armor + 1)
            {
    
    
                damage = DamageType.BailedOut;
            }
            else
            {
    
    
                if(firePower <= armor + 3)
                {
    
    
                    damage = DamageType.Destoryed;
                }
                else
                {
    
    
                    if(firePower > armor + 3)
                    damage = DamageType.AmmoDetonation;
                }
            }
        }

        return damage;
    }

这个函数名叫火力测试,来自桌游战火FOW的游戏规则,这款二战题材兵棋桌游是我的想法来源,在这款游戏中,不仅战车毁坏与否是用摇色子,是否打中也是摇色子。我使用随机数也就是为了还原这一点。当然我觉得我做的太过分了,起码要能分前后装甲吧。我之前尝试过使用组合碰撞器来实现不同模块(履带,炮塔,前装甲,后装甲),但是失败了,碰撞要能识别不同的碰撞体需要不同标签的带有刚体的游戏物体,我的敌人只能有一个刚体,那咋办嘛?我的最终方案是,实在不行的话,就按照炮弹发射的角度和碰撞到碰撞盒子的角度来间接推断。或者干脆就这样吧。


还有就是再远一点的时候完成的炮管的瞄准指示环
在这里插入图片描述
玩过坦克世界和战争雷霆的朋友肯定知道,炮塔转动是有一定速度的,游戏厂商为了让视角移动不要和炮塔转动同步以至于移动缓慢,将视角和炮塔旋转分开来。其中准心是视角要瞄准的目标方向,而炮塔会按自己的速度转到这个方向,所以我们需要一个指示器来示意炮管转到哪里了。
原理要说也不难,就是在炮管往前100个长度的位置放一个UI,然后调用WorldToScreenPoint。但是写的时候踩了蛮多坑,到现在实现的已经是最好效果了,依然不准确。代码就不放出来了。丢脸。。。


高速物体的碰撞问题,由于炮弹使用了Unity自带刚体的物流系统,所以直接用了Continuous Dynamic这个碰撞检测模式,性能差但是方便啊。呵呵。没有使用射线。
在这里插入图片描述
炮弹的刚体


到目前为止,这个项目的不足有哪些?我觉得最大的不足应该是我的技术不足,但那是没有办法的事情。但是对于我的老本行,写脚本这件事来说,我觉得我这次写的脚本很失败,当初写的时候并没有将坦克作为一个可继承的大类写出来,之后如果要加入新的坦克类型就麻烦了。现在要改就挺麻烦的。我现在相当于写了坦克有的功能,但是没有做类型区分。


但是还是要将就这样把这个项目做到一个可玩的水平,接下来要做的就是我觉得最难做的敌人AI了。


写这段话的时候项目已经完成,由于赶着期末时间比较紧,所以后续没有再写关于这个坦克项目的随手记。
简单说明一下,这个项目最后命名为《燃烧的地平线》,关于后续的AI及项目完善,详情请看:一个Unity3D制作的坦克游戏——《燃烧的地平线》

猜你喜欢

转载自blog.csdn.net/qq_37856544/article/details/110443019