Unity3D游戏开发案例学习——Tanks!(基本完结)

【2020.1.12】

概述

本案例来自unity官方中级游戏教程Tanks(单机双人坦克大战)
项目同时用于Unity机器学习内容的学习,预定计划为训练可规避障碍物,欲图消灭玩家的坦克ai

第一部分:场景设置

Lighting setting中的部分调整

1.关闭实时烘焙
2.一些未找到的设置:关闭【Backed GI】,使用【Precomputed Realtime GI】并调整实时分辨率由2至0.5
3.调整环境照明为纯色

摄像机相关设置

1.调整摄像机坐标到适宜位置,并旋转到合适的角度
2.投影模式设置为正投影
3.设置背景色,使得背景暴露在镜头中时其视觉效果符合游戏色调

第二部分:坦克的创建和控制

模型导入

对Tank设置相应的Layer属性

刚体组件

为Tank添加Rigbody组件
基本设置不变,冻结Tank的y轴位移和x、z轴旋转

碰撞器组件

为Tank添加Box Collider组件
将碰撞体中心和大小调整到适宜数值

声音资源组件

为Tank添加第一个Audio Source组件(EngineIdel)
选择声音片段EngineIdel,并设置播放属性Loop
为Tank添加第二个Audio Source组件(空置)
此声音资源组件用于Tank开火音效,在这个部分不进行具体实现

添加特效

将DustTrail预制体设置为Tank的子物体并复制,分别命名为RightDustTrail和LeftDustTrail,分别作为Tank的左右轨道移动痕迹特效

将坦克保存为预制体

将构造好的Tank保存为Prefabs

脚本

TankMovement

此脚本用于控制坦克的移动,其具体功能为:
1.获取玩家输入
2.通过代码控制音频、
3.控制坦克前后移动
4.控制坦克水平转向

具体的编程实现如下:

public int m_PlayerNumber = 1;//玩家编号(?)
	public float m_Speed = 12f;//移动速度
	public float m_TurnSpeed = 180f;//转向速度
	public AudioSource m_MovementAudio;//移动音效
	public AudioClip m_EngineIdling;//静止声音片段
	public AudioClip m_EngineDriving;//行驶声音片段
	public float m_PitchRange = 0.2f;//音高变化范围

	private string m_MovementAxisName;//移动控制轴名称(基于PlayerNumber)
	private string m_TurnAxisName;//转向控制轴名称(基于PlayerNumber)
	private Rigidbody m_Rigidbody;//刚体组件
	private float m_MovementInputValue;//移动控制输入
	private float m_TurnInputValue;//转向控制输入
	private float m_OriginalPitch;//原始音高(?)

	private void Awake()
	{
   
		m_Rigidbody = GetComponent<Rigidbody>();
	}

	private void OnEnable()
	{
   
		m_Rigidbody.isKinematic = false;//接受动力学模拟
		//重置坦克的输入值,以重新开始输入
		m_MovementInputValue = 0f;
		m_TurnInputValue = 0f;
	}

	private void OnDisable()
	{
   
		m_Rigidbody.isKinematic = true;
	}

	private void Start()
	{
   
		m_MovementAxisName = "Vertical" + m_PlayerNumber;
		//对于Player1,其移动输入的轴名称为Vertical1,以此类推
		m_TurnAxisName = "Horizontal" + m_PlayerNumber;

		m_OriginalPitch = m_MovementAudio.pitch;//存储移动音效的原始音高
	}

	private void Update()
	{
   
		//获取轴输入
		m_MovementInputValue = Input.GetAxis(m_MovementAxisName);
		m_TurnInputValue = Input.GetAxis(m_TurnAxisName);
		//
		EngineAudio();
	}

	private void EngineAudio()//控制坦克音效并调整音高
	{
   
		if (Mathf.Abs(m_MovementInputValue) < 0.1f && Mathf.Abs(m_TurnInputValue) < 0.1f)//坦克未移动
		{
   
			if (m_MovementAudio.clip == m_EngineDriving)//当前音效为移动音效
			{
   
				m_MovementAudio.clip = m_EngineIdling;//将音效切换为静止音效
				m_MovementAudio.pitch = Random.Range(m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
				//使音效控制在以原始音高为基础的适宜范围

				m_MovementAudio.Play();//重新播放音效
			}
		}
		else
		{
   
			if (m_MovementAudio.clip == m_EngineIdling)//当前音效为静止音效
			{
   
				m_MovementAudio.clip = m_EngineDriving;//将音效切换为移动音效
				m_MovementAudio.pitch = Random.Range(m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
				//使音效控制在以原始音高为基础的适宜范围

				m_MovementAudio.Play();//重新播放音效
			}
		}
	}

	private void FixedUpdate()
	{
   
		Move();
		Turn();
	}

	private void Move()//移动方法
	{
   
		Vector3 movement = transform.forward * m_MovementInputValue * m_Speed * Time.deltaTime;//移动矢量

		m_Rigidbody.MovePosition(m_Rigidbody.position + movement);//使坦克移动到指定的绝对位置
	}

	private void Turn()//转向方法
	{
   
		float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;//旋转浮点数

		Quaternion turnRotation = Quaternion.Euler(0f, turn, 0f);//?

		m_Rigidbody.MoveRotation(m_Rigidbody.rotation * turnRotation);//?
	}

完成效果图

图2.1项目进度图

存在的问题:

1.坦克旋转控制相关方法(向量、欧拉角、四元数)的有关知识
2.关于代码中变量的命名前缀"m_"
这是C#中的一种变量命名规范,m意为member,表示变量是该类成员变量

第三部分:摄像机控制

创建空物体CameraRig

1.创建空物体CameraRig,用于摄像机的控制,调整其到合适的角度
2.将Camera设置为CameraRig的子物体(此时Camera的位置信息将以CameraRig为中心),调整其坐标到适宜位置

脚本

CameraControl

此脚本用于控制摄像机的移动,注意,脚本附着于CameraRig
此脚本所控制的摄像机能够适应场景中存在多个玩家的情况

具体编程实现如下:

public float m_DampTime = 0.2f;//相机移动凝滞时间
	public float m_ScreenEdgeBuffer = 4f;//?屏幕边缘缓冲
	public float m_MinSize = 6.5f;//最小尺寸
	/*[HideInInspector]*/ public Transform[] m_Targets;//跟随目标

	private Camera m_Camera;//摄像机组件引用
	private float m_ZoomSpeed;//变焦速度
	private Vector3 m_MoveVelocity;//摄像机当前移动速度
	private Vector3 m_DesiredPosition;//期望位置(这里指摄像机在跟随多辆坦克时其中心应位于的平均位置)

	private void Awake()
	{
   
		m_Camera = GetComponentInChildren<Camera>();
	}

	private void FixedUpdate()
	{
   
		Move();
		Zoom();
	}

	private void Move()
	{
   
		FindAveragePosition();

		transform.position = Vector3.SmoothDamp(transform.position, m_DesiredPosition, ref m_MoveVelocity, m_DampTime);
		//平滑阻尼方法
		//参数释义:当前物体位置,目标物体位置,参数按引用传递的当前移动速度(方法每次调用时会修改原本数值),到达目标时间
	}

	private void FindAveragePosition()//获取平均位置
	{
   
		Vector3 averagePos = new Vector3();//有效目标平均位置向量
		int numTargets = 0;//有效目标数量

		for (int i = 0; i < m_Targets.Length; i++)
		{
   
			if (!m_Targets[i].gameObject.activeSelf)//目标游戏对象不处于活跃状态(如在一轮中死亡)
				continue;//跳过此轮循环

			averagePos += m_Targets[i].position;//累加位置
			numTargets++;//累加数量
		}

		if (numTargets > 0)
			averagePos /= numTargets;//获得平均位置

		averagePos.y = transform.position.y;//确保摄像机的y轴位置不变,即便坦克在后续修改中有y轴上的移动可能

		m_DesiredPosition = averagePos;
	}

	private void Zoom()//变焦
	{
   
		float requierdSize = FindRequierdSize();

		m_Camera.orthographicSize = Mathf.SmoothDamp(m_Camera.orthographicSize, requierdSize, ref m_ZoomSpeed, m_DampTime);
		//该语句欲图调整正交摄像机视窗大小
		//参数释义:正交摄像机当前视窗大小,所需的视窗大小,当前变焦速度,完成变焦时间
	}

	private float FindRequierdSize()
	{
   
		Vector3 desiredLocalPos = transform.InverseTransformPoint(m_DesiredPosition);
		//A.InverseTransformPoint(B)
		//该方法返回B以A为世界坐标原点时的坐标向量,即B相对A的坐标
		//再这里通过此方法获取期望位置相对CameraRig的坐标

		float size = 0f;

		for (int i = 0; i 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值