Unity3D实现坦克大战

一、效果图演示

二、逻辑剖析

从界面上:

  • 需要一个Canvas满屏对着用户,该Canvas上展示用户的游戏数据,比如血条。
  • 需要一个Canvas放在蓝色坦克上方,也需要实时对着用户,显示敌人的血条信息
  • 两个坦克
  • 一个平面Plane放草地的纹理

从逻辑上:

  • 前后箭头键控制玩家前进或后退
  • 左右箭头键控制玩家左右转向
  • 鼠标左键或空格键控制玩家发射炮弹
  • 玩家血条希纳是在屏幕左上角
  • 相机在玩家后上方的位置,始终跟随玩家,朝玩家正前方看
  • 玩家移动时,敌人转向玩家,当偏离玩家的角度小于5度时,发射炮弹
  • 敌人血条显示在其上方,并且始终看向相机

三、界面组件信息

(1)游戏对象层级结构

(2)组件参数信息

1.玩家Player组件参数

NameTypePositionRotationScaleColor
PlayerEmpty(0, 0.25, -5)(0, 0, 0)(1, 1, 1)#228439
ButtonCube(0, 0, 0)(0, 0, 0)(2, 0.5, 2)#228439
TopCube(0, 0.5, 0)(0, 0, 0)(1, 0.5, 1)#228439
GunCylinder(0, 0, 1.5)(90, 0, 0)(0.2, 1, 0.4)#228439
FirePointEmpty(0, 1.15, 0)(0, 0, 0)(1, 1, 1)--

Player 游戏对象添加了刚体组件,并修改 Mass = 100,Drag = 1,AngularDrag = 0.1,Freeze Rotation 中勾选 X 和 Z。 

2.玩家HP组件参数

NameTypePositionWidth/HeightColor
PlayerHPCanvas(960, 540, 0)1920/1080--
Panel     Panel位置信息全是0#FFFFFF
HealthBGImage(-809,464,0)200/20#FFFFFF
HealthImage(-809,464,0)200/20#FF2230

玩家 PlayerHP 的 Canvas 渲染模式是 Screen Space - Overlay。

制作一个红色的图片放入Health的Source Image中,Health 的 ImageType 设置为 Filled,Fill Method 设置为 Horizontal。

3.敌人组件参数

NameTypePositionRotationScaleColor
EnemyEmpty(0, 0.25, 5)(0, 180, 0)(1, 1, 1)#15D3F9
ButtonCube(0, 0, 0)(0, 0, 0)(2, 0.5, 2)#15D3F9
TopCube(0, 0.5, 0)(0, 0, 0)(1, 0.5, 1)#15D3F9
GunCylinder(0, 0, 1.5)(90, 0, 0)(0.2, 1, 0.4)#15D3F9
FirePointEmpty(0, 1.15, 0)(0, 0, 0)(1, 1, 1)--

Enemy 游戏对象添加了刚体组件,并修改 Mass = 100,Drag = 0.5,AngularDrag = 0.1,Freeze Rotation 中勾选 X 和 Z。  

4.敌人HP组件参数

NameTypePositionWidth/HeightColor
HPCanvas(0, 0.85, 0)2/0.2--
HealthBGImage(0,0,0)2/0.2#FFFFFF
HealthImage(0,0,0)2/0.2#FF2230

敌人 HP 的 Canvas 渲染模式是 World Space,将刚才的红色底图也放入Health的Source Image中,Health 的 ImageType 设置为 Filled,Fill Method 设置为 Horizontal。

5.地面和炮弹的组件参数

NameTypePositionRotationScaleColor
PlanePlane(0, 0, 0)(0, 0, 0)(10, 10, 10)GrassRockyAlbedo
BulletSphere(0, 0.5, -5)(0, 0, 0)(0.3, 0.3, 0.3)#228439

炮弹作为预设体拖拽到 Assets/Resources/Prefabs 目录下,并且添加了刚体组件。

四、脚本代码:

1.CameraController

 CameraController 脚本组件挂在 MainCamera 游戏对象上。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CameraController : MonoBehaviour
{// Start is called before the first frame updateprivate Transform player; // 玩家private Vector3 relaPlayerPos; // 相机在玩家坐标系中的位置private float targetDistance = 15f; // 相机看向玩家前方的位置void Start(){relaPlayerPos = new Vector3(0, 4, -8);player = GameObject.Find("Player/Top").transform; // 世界坐标系位置}private void LateUpdate(){ComCameraPos();}// 计算相机坐标private void ComCameraPos(){Vector3 target = player.position + player.forward * targetDistance;transform.position = transformVecter(relaPlayerPos, player.position, player.right, player.up, player.forward);transform.rotation = Quaternion.LookRotation(target - transform.position);}// 以origin为原点,已知vec在坐标轴locX/locY/locZ中的位置,将vec转为世界坐标系的位置private Vector3 transformVecter(Vector3 vec, Vector3 origin, Vector3 locX, Vector3 locY, Vector3 locZ) { return vec.x * locX + vec.y * locY + vec.z * locZ + origin;}
}

2.BulletInfo

using UnityEngine;public class BulletInfo
{public string name; // 炮弹名public Color color; // 炮弹颜色public Vector3 flyDir; // 炮弹飞出方向public float speed; // 炮弹飞行速度public float fireRange; // 炮弹射程public BulletInfo(string name, Color color, Vector3 flyDir, float speed, float fireRange){this.name = name;this.color = color;this.flyDir = flyDir;this.speed = speed;this.fireRange = fireRange;}
}

3.BulletController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class BulletController : MonoBehaviour
{private BulletInfo bulletInfo; // 炮弹信息private volatile bool isDying = false;// Start is called before the first frame updatevoid Start(){gameObject.name = bulletInfo.name;GetComponent<MeshRenderer>().material.color = bulletInfo.color;float lifeTime = bulletInfo.fireRange / bulletInfo.speed; // 存活时间Destroy(gameObject, lifeTime);}// Update is called once per framevoid Update(){transform.GetComponent<Rigidbody>().velocity = bulletInfo.flyDir * bulletInfo.speed;}public void SetBulletInfo(BulletInfo bulletInfo){this.bulletInfo = bulletInfo;}private void OnCollisionEnter(Collision other){if (isDying){return;}if(IsHitEnemy(gameObject.name, other.gameObject.name)){other.transform.Find("HP/Health").GetComponent<Image>().fillAmount -= 0.1f;isDying = true;Destroy(gameObject, 0.1f);}else if(IsHitPlayer(gameObject.name, other.gameObject.name)){GameObject.Find("PlayerHP/Panel/Health").GetComponent<Image>().fillAmount -= 0.1f;isDying = true;Destroy(gameObject, 0.1f);}}private bool IsHitEnemy(string name, string otherName){return name.Equals("PlayerBullet") && otherName.Equals("Enemy");}private bool IsHitPlayer(string name, string otherName){return name.Equals("EnemyBullet") && otherName.Equals("Player");}
}

4.PlayerController

PlayerController 脚本组件挂在 Player 游戏对象上。 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerController : MonoBehaviour
{private Transform firePoint; // 开火点private GameObject bulletPrefab; // 炮弹预设体private float tankMoveSpeed = 4f; // 坦克移动速度private float tankRotateSpeed = 2f; // 坦克转向速度private float fireWaitTime = float.MaxValue; // 距离上次开火已等待的时间private float bulletCoolTime = 0.15f; // 炮弹冷却时间void Start(){firePoint = transform.Find("Top/Gun/FirePoint");bulletPrefab = (GameObject)Resources.Load("Prefabs/Bullet");}// Update is called once per framevoid Update(){fireWaitTime += Time.deltaTime;float hor = Input.GetAxis("Horizontal");float ver = Input.GetAxis("Vertical");Move(hor, ver);if(Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space)){Fire();}}// 坦克移动private void Move(float hor, float ver) {if (Mathf.Abs(hor) > 0.1f || Mathf.Abs(ver) > 0.1f) {GetComponent<Rigidbody>().velocity = transform.forward * tankMoveSpeed * ver;GetComponent<Rigidbody>().angularVelocity = Vector3.up * tankRotateSpeed * hor;}}// 开炮private void Fire(){if (fireWaitTime > bulletCoolTime) { BulletInfo bulletInfo = new BulletInfo("PlayerBullet", Color.red, transform.forward, 10f, 15f);GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity);bullet.AddComponent<BulletController>().SetBulletInfo(bulletInfo);fireWaitTime = 0f;}}
}

5.EnemyController

EnemyController 脚本组件挂在 Enemy 游戏对象上。  

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EnemyController : MonoBehaviour
{private Transform target; // 目标private Transform top; // 炮头private Transform firePoint; // 开火点private Transform hp; // 血条private GameObject bulletPrefab; // 炮弹预设体private float rotateSpeed = 0.4f; // 坦克转向速度private float fireWaitTime = float.MaxValue; // 距离上次开火已等待时间private float bulletCoolTime = 1f; // 炮弹冷却时间// Start is called before the first frame updatevoid Start(){target = GameObject.Find("Player/Top").transform;top = transform.Find("Top");firePoint = transform.Find("Top/Gun/FirePoint");hp = transform.Find("HP");bulletPrefab = (GameObject)Resources.Load("Prefabs/Bullet");}// Update is called once per framevoid Update(){fireWaitTime += Time.deltaTime;if (LookAtTarget()) {Fire();}HPLookAtCamera();}private bool LookAtTarget(){Vector3 dir = target.position - top.position;float angle = Vector3.Angle(dir, top.forward);if(angle > 5){int axis = Vector3.Dot(Vector3.Cross(dir, top.forward), Vector3.up) > 0 ? -1 : 1;GetComponent<Rigidbody>().angularVelocity = axis * Vector3.up * rotateSpeed;return false;}GetComponent<Rigidbody>().velocity = Vector3.zero;return true;}private void HPLookAtCamera(){Vector3 cameraPos = Camera.main.transform.position;Vector3 target = new Vector3(cameraPos.x, hp.position.y, cameraPos.z);hp.LookAt(target);}private void Fire(){if(fireWaitTime > bulletCoolTime){BulletInfo bulletInfo = new BulletInfo("EnemyBullet", Color.yellow, top.forward, 5f, 10f);GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity); // 通过预设体创建炮弹bullet.AddComponent<BulletController>().SetBulletInfo(bulletInfo);fireWaitTime = 0;}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/251845.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++并发编程 -2.线程间共享数据

本章就以在C中进行安全的数据共享为主题。避免上述及其他潜在问题的发生的同时&#xff0c;将共享数据的优势发挥到最大。 一. 锁分类和使用 按照用途分为互斥、递归、读写、自旋、条件变量。本章节着重介绍前四种&#xff0c;条件变量后续章节单独介绍。 由于锁无法进行拷贝…

三层交换组网实验(华为)

思科设备参考&#xff1a;三层交换组网实验&#xff08;思科&#xff09; 一&#xff0c;技术简介 三层交换技术的出现&#xff0c;解决子网必须依赖路由器进行管理的问题&#xff0c;解决传统路由器低速、复杂所造成的网络瓶颈问题。一个具有三层交换功能的设备可简单理解为…

蓝桥杯每日一题-----数位dp练习

题目 链接 参考代码 写了两个&#xff0c;一个是很久以前写的&#xff0c;一个是最近刚写的&#xff0c;很久以前写的时候还不会数位dp所以写了比较详细的注释&#xff0c;这两个代码主要是设置了不同的记忆数组&#xff0c;通过这两个代码可以理解记忆数组设置的灵活性。 im…

npm ERR! code CERT_HAS_EXPIRED

执行npm i报错&#xff1a; npm ERR! code ETIMEDOUT npm ERR! syscall connect npm ERR! errno ETIMEDOUT npm ERR! network request to https://registry.npmjs.org/react-redux failed, reason: connect ETIMEDOUT 104.16.2.35:443 npm ERR! network This is a problem rel…

python常用pandas函数nlargest / nsmallest及其手动实现

目录 pandas库 Series和DataFrame nlargest和nsmallest 用法示例 代替方法 手动实现 模拟代码 pandas库 是Python中一个非常强大的数据处理库&#xff0c;提供了高效的数据分析方法和数据结构。它特别适用于处理具有关系型数据或带标签数据的情况&#xff0c;同时在时间…

十分钟学会用springboot制作微信小程序富文本编辑器

1.1 富文本模型设计 在构建富文本编辑器系统时&#xff0c;首先需要设计一个合适的富文本模型。 CREATE TABLE IF NOT EXISTS rich_texts (id INT PRIMARY KEY AUTO_INCREMENT,title VARCHAR(255),content TEXT,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );这个表包括…

【算法与数据结构】647、516、LeetCode回文子串+最长回文子序列

文章目录 一、647、回文子串二、516、最长回文子序列三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、647、回文子串 思路分析&#xff1a;判断一个字符串是否为回文串那么必须确定回文串的所在区间&#xff0c;而一维…

(2)(2.13) Rockblock Satellite Modem

文章目录 前言 1 支持的MAVLink命令信息 2 设置 3 使用方法 4 数据成本 5 参数 前言 &#xff01;Note 该功能仅适用于 ArduPilot 4.4 或更高版本&#xff0c;并且要求飞行控制器支持 LUA 脚本(LUA Scripts)。 RockBLOCK 卫星调制解调器可实现与 ArduPilot 飞行器的全球…

如何将pdf转换成ppt?掌握这个方法就简单多了

有时候&#xff0c;PDF文件的布局和设计可能需要进行微调或重新排版&#xff0c;以适应PPT的特定格式和风格。那么怎么pdf怎么转ppt呢&#xff1f;为了更方便地对布局、字体、图像和其他元素进行编辑和调整&#xff0c;以符合PPT的需求&#xff0c;我们可以直接通过pdf在线转pp…

SQL Server之DML触发器

一、如何创建一个触发器呢 触发器的定义语言如下&#xff1a; CREATE [ OR ALTER ] TRIGGER trigger_nameon {table_name | view_name}{for | After | Instead of }[ insert, update,delete ]assql_statement从这个定义语言我们可以知道如下信息&#xff1a; trigger_name&…

Linux项目自动化构建工具之make/Makefile演示gcc编译

文章目录 一、背景二、如何使用&#xff1f;三、原理四、关于make的问题五、再次理解/编写makefile依赖关系依赖方法 六、原理讲解项目清理makefile是支持变量的取消执行make后显示命令依赖方法可以多行 一、背景 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备…

nop-entropy可逆计算入门(1)

第1步&#xff1a;从大佬的gitee&#xff1a;https://gitee.com/canonical-entropy/nop-entropy下载源码&#xff0c;进行本地编译&#xff0c;具体编译看项目下的readme,想偷懒的可以下载我编译后的jar&#xff0c;放到自己的maven仓库 https://pan.baidu.com/s/1p9MOh40MJ2m…

mac如何实现升级node版本、切换node版本

一、 查看node所有版本&#xff08;前提:安装了nodejs&#xff09; npm view node versions二、安装指定node版本 sudo n 版本号三、检查目前安装了哪些版本的node&#xff0c;会出现已安装的node版本 n四、切换已安装的node版本 sudo n 版本号其他命令 1、sudo npm cache…

Centos 内存和硬盘占用情况以及top作用

目录 只查看内存使用情况&#xff1a; 内存使用排序取前5个&#xff1a; 硬盘占用情况 定位占用空间最大目录 top查看cpu及内存使用信息 前言-与正文无关 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&…

Matlab数字图像处理——图像复原与滤波算法应用方法

图像处理领域一直以来都是计算机科学和工程学的一个重要方向&#xff0c;图像复原则是其中一个重要的研究方向之一。图像复原旨在通过运用各种滤波算法&#xff0c;对图像进行去噪、恢复和改善&#xff0c;以提高图像的质量和可视化效果。在本文中&#xff0c;我们将介绍如下内…

python Flask 写一个简易的 web 端程序(附demo)

python Flask 写一个简易的 web 端程序 &#xff08;附demo&#xff09; 介绍简单介绍装饰器 app.route("/") 进阶增加接口设置端口 静态网页核心代码完整代码 介绍 Flask 是一个用于构建 Web 应用程序的轻量级 Python Web 框架。它设计简单、易于学习和使用&#x…

ReactNative实现宽度变化实现的动画效果

效果如上图所示&#xff0c;通过修改设备宽度实现动画效果 import React, {useRef, useEffect, useState} from react; import {Animated, Text, View, Image} from react-native;const FadeInView props > {const fadeAnim useRef(new Animated.Value(0)).current;React…

list基本使用

list基本使用 构造迭代器容量访问修改 list容器底层是带头双向链表结构&#xff0c;可以在常数范围内在任意位置进行输入和删除&#xff0c;但不支持任意位置的随机访问&#xff08;如不支持[ ]下标访问&#xff09;&#xff0c;下面介绍list容器的基本使用接口。 template <…

k8s学习-Kubernetes的网络

Kubernetes作为编排引擎管理着分布在不同节点上的容器和Pod。Pod、Service、外部组件之间需要⼀种可靠的方找到彼此并进行通信&#xff0c;Kubernetes网络则负责提供这个保障。 1.1 Kubernetes网络模型 Container-to-Container的网络 当Pod被调度到某个节点&#xff0c;Pod中…

ARM PAC指针认证的侧信道攻击——PACMAN安全漏洞

目录 Q1. PACMAN论文的内容是什么&#xff1f; Q2. Arm处理器是否存在漏洞&#xff1f; Q3. 受Arm合作伙伴架构许可设计的处理器实现是否受到影响&#xff1f; Q4. Cortex-M85受到影响吗&#xff1f; Q5. Cortex-R82受到影响吗&#xff1f; Q6. 指针认证如何保护软件&…