阶段学习总结-坦克大战案例
今天学习了一个案例-坦克大战,最终跟着视频做出了一个小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() { } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!