最近这一个星期学了unity的脚本开发 和transform 刚体 碰撞器 触发器 这些组件。就用新学的知识做一个坦克大战吧。
项目资源在这里哦自取
链接:https://pan.baidu.com/s/1_5u4TKzCVpL4LtA4Ev6ayA
提取码:g12v
–来自百度网盘超级会员V2的分享
一: 首先。我们先用unity自带的组件搭建场景吧!
用plane和一些cube 做成了一个小范围。用圆柱体和两个cube搭建一个玩家坦克(player)
因为我们要有很多敌人,所有我们把敌人坦克做成预制件。
同样方法吧玩家的炮弹和敌人的炮弹也做成预制件
我们用子弹打敌人,子弹可以设置成触发器。
并把敌人加标签
同样道理 敌人的炮弹打玩家,敌人的炮弹加触发器。玩家加标签。
好了我们的场景搭建就先说到这儿吧。
二:现在我们要实现的是玩家可以用过wsad控制移动和旋转。按空格可以开炮。此标题下脚本都挂载在player上
脚本:
public class TankPlayer : MonoBehaviour {
private float hor;
private float ver;
private bool fire;//是否开炮
public int life;
[HideInInspector]
public bool invincibleFlag;
public float moveSpeed;
public float turnSpeed;
[Header("子弹预制件")]
public GameObject bulletPlayer;
private Vector3 bulletPos;
void Start () {
moveSpeed = 10;
turnSpeed = 2;
life = 100;
invincibleFlag = false;
}
// Update is called once per frame
void Update () {
hor = Input.GetAxis("Horizontal");
ver = Input.GetAxis("Vertical");
fire = Input.GetButtonDown("Fire");
transform.position += ver * transform.forward * moveSpeed * Time.deltaTime;
this.transform.position = new Vector3(transform.position.x, 0, transform.position.z);
transform.Rotate(Vector3.up * hor * turnSpeed);
if (fire)
{
bulletPos = GameObject.Find("player/fire/firePos").transform.position;
GameObject bullet = Instantiate(bulletPlayer,bulletPos,Quaternion.identity);
}
if (life <= 0)
{
Destroy(this.gameObject);
}
}
private void FixedUpdate()
{
GetComponent<Rigidbody>().velocity = Vector3.zero;
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "wall")
{
transform.eulerAngles -= new Vector3(0, 180, 0);
}
}
private void OnTriggerEnter(Collider other)
{
// Debug.Log(other.gameObject.name);
if (other.tag == "imprisoned")
{
Destroy(other.gameObject);
GetComponent<InprisonedClass>().enabled = true;
}
if(other.tag== "invincible")
{
Destroy(other.gameObject);
GetComponent<InvincibleClass>().enabled = true;
}
if (other.tag == "allDeid")
{
Destroy(other.gameObject);
GetComponent<AllDeidClass>().enabled = true;
}
}
这里要说明的是,要提前去edit/projectSetting 中的Input中设置水平轴 竖直轴 开火键。
小细节:在碰撞检测中检测碰到墙就转头。
在触发器中检测,是否触发了 道具。其中道具是预制件。并且挂在了标签。如果触发了道具,将销毁道具对象,并且使道具的对应脚本启动,开始实现道具的功能
第一个道具是紧固所有敌人。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InprisonedClass : MonoBehaviour {
private float time;
private List<GameObject> enemys;
private bool enemyCanMoveFlag;
// Use this for initialization
void Start () {
time = 5;
enemyCanMoveFlag = false;
enabled = false;
}
// Update is called once per frame
void Update () {
time -= Time.deltaTime;
GameObject[] enemys = GameObject.FindGameObjectsWithTag("enemy");
if (time > 0)
{
enemyCanMoveFlag = false;
for (int i = 0; i < enemys.Length; i++)
{
enemys[i].GetComponent<EnemyControl>().enabled = enemyCanMoveFlag;
}
}
else
{
enemyCanMoveFlag = true;
time = 5;
for (int i = 0; i < enemys.Length; i++)
{
enemys[i].GetComponent<EnemyControl>().enabled = enemyCanMoveFlag;
}
enabled = false;
}
}
}
这里是把控制敌人的方法设置为不可用,这样敌人就无法移动了。时间是五秒。五秒以后敌人就可以动了
执行完以后,再把这个方法禁用,等待下次触发禁锢道具时在启用。
第二个道具是使玩家无敌
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InvincibleClass : MonoBehaviour {
private float time ;
// Use this for initialization
void Start () {
time = 5;
enabled = false;
}
// Update is called once per frame
void Update () {
time -= Time.deltaTime;
if (time > 0)
{
this.gameObject.GetComponent<TankPlayer>().invincibleFlag = true;
}
else
{
this.gameObject.GetComponent<TankPlayer>().invincibleFlag = false;
time = 5;
enabled = false;
}
}
}
这里是玩家无敌五秒。直接设置玩家是否无敌bool值为true,并在玩家类中进行判断。五秒后,设置玩家是否无敌为false 并设置此方法为false,等待下一次触发
第三个道具是 杀灭全场所有敌人。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AllDeidClass : MonoBehaviour {
private GameObject[] enemys;
// Use this for initialization
void Start () {
enabled = false;
}
// Update is called once per frame
void Update () {
enemys = GameObject.FindGameObjectsWithTag("enemy");
for (int i = 0; i < enemys.Length; i++)
{
Destroy(enemys[i].gameObject);
}
enabled = false;
}
}
找到所有标签为敌人的对象 并且全部销毁。
三:敌人AI的生成和控制。
敌人AI的生成 脚本挂载在空对象或摄像机上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyLife : MonoBehaviour
{
[Header("敌人预制件")]
public GameObject enemyPrefab;
private Vector3 positionEnemy;
private float timeGenerate;
private int num;
// Use this for initialization
void Start()
{
num = 25;
timeGenerate = 3f;
}
private void LateUpdate()
{
timeGenerate -= Time.deltaTime;
if ( timeGenerate< 0)
{
Generate();
timeGenerate = 3;
}
}
/// <summary>
/// 生成坦克
/// </summary>
public void Generate()
{
if(num>0)
{
num--;
do
{
positionEnemy = new Vector3(Random.Range(-48, -21), 1, Random.Range(48, 21));
}
while (false);
Instantiate(enemyPrefab, positionEnemy, Quaternion.Euler(0, 180, 0));
}
}
/// <summary>
/// 这个位置能不能用
/// </summary>
/// <param name="pos">位置</param>
/// <returns></returns>
public bool CanotUsePos(Vector3 pos)
{
return Physics.CheckSphere(pos, 2, ~(1 << 8));
}
}
在随机生成的时候 我们要随位置进行,物理检测,这个位置一定范围内不可以有其他敌人。CanotUsePos()方法中的Physics.CheckSphere(pos, 2, ~(1 << 8));是检测在pos的两米范围内,除了第八层的所有层的对象。
其中地板加layer,为第八层
敌人AI的控制 挂载在敌人上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyControl : MonoBehaviour {
public GameObject bulletPerfab;
private GameObject player;
private Transform playerTransform;
private float dis;
private float timeBullet;
// Use this for initialization
void Start () {
timeBullet = 0;
dis = 0;
gameObject.GetComponent<MoveEnemy>().flag = false;
}
// Update is called once per frame
void Update () {
player = GameObject.Find("player");
playerTransform = player.transform;
dis = Vector3.Distance(transform.position, playerTransform.position);
if (dis > 20)
{
//TODO:坦克巡逻
gameObject.GetComponent<MoveEnemy>().flag = true;
}else if (dis < 20 && dis > 10)
{
//向前逼近
gameObject.GetComponent<MoveEnemy>().flag = false;
TurnToPlayer();
GoToPlayer();
}
else if (dis < 10)
{
gameObject.GetComponent<MoveEnemy>().flag = false;
//转向玩家开炮
TurnToPlayer();
FirePlayer();
}
}
/// <summary>
/// 逼近玩家
/// </summary>
private void GoToPlayer()
{
if (!ForwordHaveFriend())
{
this.transform.position += transform.forward * GetComponent<MoveEnemy>().speed * Time.deltaTime;
}
this.transform.position = new Vector3(transform.position.x, 1, transform.position.z);
}
/// <summary>
/// 前方有敌人
/// </summary>
/// <returns>true:有false无</returns>
private bool ForwordHaveFriend()
{
Ray ray = new Ray(transform.position, transform.forward * 3);
if (Physics.Raycast(ray, 3, (1 << 9)))
{
return true;
}
return false;
}
private void FirePlayer()
{
timeBullet -= Time.deltaTime;
if (timeBullet < 0)
{
timeBullet = 3;
Vector3 pos = transform.Find("fire/firePos").transform.position;
GameObject bullet = Instantiate(bulletPerfab, pos, Quaternion.identity);
bullet.GetComponent<BulletEnemy>().forword_Bullet = this.transform;
}
}
private void TurnToPlayer()
{
Vector3 playerPos = player.transform.position;
Quaternion target = Quaternion.LookRotation(playerPos - transform.position);
transform.rotation = Quaternion.Lerp(transform.rotation, target, GetComponent<MoveEnemy>().smooth);
}
}
其中MoveEnemy是巡逻类。
敌人AI的巡逻类 挂载在敌人上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveEnemy : MonoBehaviour {
private float angle;
public float speed;
public float smooth;
private float timeTurn ;
[HideInInspector]
public bool flag;
//目标四元数
Quaternion target;
// Use this for initialization
void Start () {
smooth = 2;
speed = 2;
timeTurn = 1;
flag = false;
}
// Update is called once per frame
void Update () {
if (flag)
{
timeTurn -= Time.deltaTime;
if (timeTurn < 0)
{
angle = Random.Range(0, 360);
target = Quaternion.Euler(0, angle, 0);
transform.rotation = Quaternion.Lerp(transform.rotation, target, smooth * Time.deltaTime);
timeTurn = 0.5f;
}
GetComponent<Rigidbody>().velocity = Vector3.zero;
this.transform.position += transform.forward * speed * Time.deltaTime;
this.transform.position = new Vector3(transform.position.x, 1, transform.position.z);
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "wall")
{
transform.eulerAngles -= new Vector3(0, 180, 0);
}
}
private void FixedUpdate()
{
GetComponent<Rigidbody>().velocity = Vector3.zero;
}
}
四:炮弹的脚本
玩家 的炮弹:挂载在玩家炮弹的预制件上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletPlayer : MonoBehaviour {
public float speed;
public Transform player;
private Vector3 forword_Bullet;
// Use this for initialization
void Start () {
speed = 20;
player = GameObject.FindGameObjectWithTag("player").transform;
forword_Bullet = player.forward;
transform.GetComponent<Rigidbody>().velocity = forword_Bullet * speed;
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.transform.root.tag== "enemy")
{
Destroy(other.gameObject.transform.root.gameObject);
Destroy(this.gameObject);
}
if (other.gameObject.tag == "wall" || other.gameObject.tag == "floor")
{
Destroy(this.gameObject);
}
}
}
敌人的炮弹 挂载在敌人炮弹的预制件上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletEnemy : MonoBehaviour {
public float speed;
public Transform forword_Bullet;
// Use this for initialization
void Start () {
speed = 20;
transform.GetComponent<Rigidbody>().velocity = forword_Bullet.forward * speed;
}
private void OnTriggerEnter(Collider other)
{
if (other.transform.root.tag == "player")
{
if (!other.transform.root.GetComponent<TankPlayer>().invincibleFlag)
{
other.transform.root.GetComponent<TankPlayer>().life -= 1;
Destroy(this.gameObject);
}
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "wall" || collision.gameObject.tag == "floor")
{
Destroy(this.gameObject);
}
}
}
五:摄像机跟随 脚本挂载在摄像机上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class followComera : MonoBehaviour {
public float upDis;
public float backDis;
public float turnSpeed;
private Vector3 positionC;
private Transform follow;
// Use this for initialization
void Start () {
upDis = 10;
backDis = 20;
turnSpeed = 2;
follow = GameObject.FindWithTag("player").transform;
}
// Update is called once per frame
void Update () {
positionC = follow.position + upDis * Vector3.up - backDis * follow.forward;
transform.position = Vector3.Lerp(transform.position, positionC, turnSpeed * Time.deltaTime);
transform.LookAt(follow);
}
}
六:生成道具的脚本,挂载在空对象或摄像机上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Prop : MonoBehaviour {
private float timeP;
/// <summary>
/// 无敌
/// </summary>
public GameObject invinciblePrefab;
public GameObject allDiedPrefab;
/// <summary>
/// 禁锢
/// </summary>
public GameObject imprisonedPrefab;
private float hight;
// Use this for initialization
void Start () {
timeP = 0;
hight = GameObject.Find("hightProp").transform.position.y;
}
// Update is called once per frame
void Update () {
timeP -= Time.deltaTime;
if (timeP <= 0)
{
timeP = 5;
int x = Random.Range(-20, 20);
int z = Random.Range(-20, 20);
int num = Random.Range(1, 100);
if (num <=50 && num > 1)
{
Instantiate(imprisonedPrefab, new Vector3(x, hight, z), Quaternion.identity);
}else if (num > 50 && num <= 90)
{
Instantiate(invinciblePrefab, new Vector3(x, hight, z), Quaternion.identity);
}else if (num > 90 && num <= 95)
{
Instantiate(allDiedPrefab, new Vector3(z, hight, z), Quaternion.identity);
}
}
}
}
好了 我们的坦克大战开发完成了 看一下效果吧。