2D坦克大战不是在Canvas下开发的,而是直接用SpriteRenderer来。
2D坦克大战核心功能是2D地图的生成,有:草、河流、土墙、钢铁墙、空气墙元素以及一个特殊的物体(出生效果),在生成地图的时候需要将地图视为一个二维空间,每个坐标都是一个资源,使用了的坐标将它放入一个列表保存,以便于随机出可用的坐标,不然同一个地方会生成多个地图元素。
地图是随机生成的,使用一个脚本去生成地图,地图大小可自己定义,其中基地和基地周围的墙生成是固定的,空气墙如果地图大小固定的话,生成方法也是固定的,代码如下,一般的话可以看得懂。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapCreation : MonoBehaviour {
//各种场景物体
//0.老家 1.墙 2.障碍 3.出生效果 4.河流 5.草 6.空气墙
public GameObject[] item;
//已有物体位置的列表标志
private List<Vector3> itemPositionList = new List<Vector3>();
private void Awake()
{
//实例化地图和玩家
InitMap();
}
//实例化地图和玩家
private void InitMap()
{
//实例化老家
CreateItem(item[0], new Vector3(0, -8, 0), Quaternion.identity);
//用墙把老家围起来
CreateItem(item[1], new Vector3(-1, -8, 0), Quaternion.identity);
CreateItem(item[1], new Vector3(1, -8, 0), Quaternion.identity);
for (int i = -1; i < 2; i++)
{
CreateItem(item[1], new Vector3(i, -7, 0), Quaternion.identity);
}
//实例化外围墙
for (int x = -11; x <= 11; x++)
{
CreateItem(item[6], new Vector3(x, 9, 0), Quaternion.identity);
CreateItem(item[6], new Vector3(x, -9, 0), Quaternion.identity);
}
for (int y = -9; y <= 9; y++)
{
CreateItem(item[6], new Vector3(-11, y, 0), Quaternion.identity);
CreateItem(item[6], new Vector3(11, y, 0), Quaternion.identity);
}
//初始化玩家
GameObject go = Instantiate(item[3], new Vector3(-2, -8, 0), Quaternion.identity);
go.GetComponent<Born>().createPlayer = true;
//产生敌人
CreateItem(item[3], new Vector3(-10, 8, 0), Quaternion.identity);
CreateItem(item[3], new Vector3(0, 8, 0), Quaternion.identity);
CreateItem(item[3], new Vector3(10, 8, 0), Quaternion.identity);
InvokeRepeating("CreateEnemy", 4, 5);
//实例化地图
for (int i = 0; i < 60; i++)
{
CreateItem(item[1], CreateRandomPosition(), Quaternion.identity);
}
for (int i = 0; i < 20; i++)
{
CreateItem(item[2], CreateRandomPosition(), Quaternion.identity);
}
for (int i = 0; i < 20; i++)
{
CreateItem(item[4], CreateRandomPosition(), Quaternion.identity);
}
for (int i = 0; i < 20; i++)
{
CreateItem(item[5], CreateRandomPosition(), Quaternion.identity);
}
}
private void CreateItem(GameObject createGameObject,Vector3 createPosition,Quaternion rotation)
{
GameObject itemGo = Instantiate(createGameObject, createPosition, rotation);
itemGo.transform.SetParent(gameObject.transform);
itemPositionList.Add(createPosition);
}
//产生随机位置的方法
private Vector3 CreateRandomPosition()
{
//不生成x=-10 ,10两列,y=8,-8这一行的位置
while(true)
{
Vector3 createPosition = new Vector3(Random.Range(-9, 10), Random.Range(-7, 8), 0);
if (!HasThePosition(createPosition))//如果没有该位置在位置列表中,那么可以产生出这个随机位置
{
return createPosition;
}
}
}
//判断位置列表中是否有该位置
private bool HasThePosition(Vector3 createPos)
{
for(int i=0;i<itemPositionList.Count;i++)
{
if(createPos==itemPositionList[i])
{
return true;
}
}
return false;
}
/// <summary>
/// 生成一个敌人在随机三个固定的位置.
/// </summary>
private void CreateEnemy()
{
int num = Random.Range(0, 3);
Vector3 enemyPos = new Vector3();
if(num==0)
{
enemyPos = new Vector3(-10, 8, 0);
}
else if (num ==1)
{
enemyPos = new Vector3(0, 8, 0);
}
else if (num == 2)
{
enemyPos = new Vector3(10, 8, 0);
}
CreateItem(item[3], enemyPos, Quaternion.identity);
}
}
其中,出生效果是一个挂有Born脚本的物体,可理解成是一个孵化器,可生成玩家坦克或敌人坦克,而玩家坦克身上有玩家脚本,控制玩家移动和死亡等逻辑,敌人坦克有敌人脚本,敌人用简单的逻辑去跑动和攻击之类的以及死亡之类的。直接放代码看吧。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour {
public AudioSource moveAudio;
public AudioClip[] tankAudio;
public float speed = 10f;
public Sprite[] tankSprites;//上 右 下 左
public GameObject bullet;
public GameObject explosionPrefab;
private SpriteRenderer sr;
private Vector3 bulletEulerAngles;
private float timeVal;
private float defendTimeVal=3;
private bool isDefended = true;
public GameObject defendEffectPrefab;
private void Awake()
{
sr = GetComponent<SpriteRenderer>();
}
void Start () {
}
//不固定
void Update () {
//是否处于无敌状态
if (isDefended)
{
defendEffectPrefab.SetActive(true);
defendTimeVal -= Time.deltaTime;
if(defendTimeVal<=0)
{
defendEffectPrefab.SetActive(false);
isDefended = false;
}
}
}
//Update后执行,固定时间执行0.02s执行一次
private void FixedUpdate()
{
//如果游戏失败了,那么禁止玩家的一切行为,即移动,发射子弹
if(PlayerManager.Instance.isDefeat)
{
return;
}
Move();
//至少等0.4秒发射间隔时间
if (timeVal >= 0.4f)
{
Attack();
}
else
{
timeVal += Time.fixedDeltaTime;
}
}
//坦克攻击
private void Attack()
{
if(Input.GetKeyDown(KeyCode.Space))
{
//子弹产生的角度:当前坦克角度+子弹应该旋转的角度
Instantiate(bullet, transform.position, Quaternion.Euler(transform.eulerAngles+bulletEulerAngles));
timeVal = 0;
}
}
//坦克移动
private void Move()
{
float v = Input.GetAxisRaw("Vertical");
transform.Translate(Vector3.up * v * speed * Time.fixedDeltaTime, Space.World);
if (v < 0)
{
sr.sprite = tankSprites[2];
bulletEulerAngles = new Vector3(0, 0, -180);
}
else if (v > 0)
{
sr.sprite = tankSprites[0];
bulletEulerAngles = new Vector3(0, 0, 0);
}
if(Mathf.Abs(v)>0.05f)
{
moveAudio.clip = tankAudio[1];
if(!moveAudio.isPlaying)
{
moveAudio.Play();
}
}
if (v != 0)
{
return;//意味着 玩家按着上或者下,只能上下移动,而无法再会执行左右操作
}
float h = Input.GetAxisRaw("Horizontal");//h就是D按下或者→按下时是1,←或者A按下是-1,让物体左右移动
transform.Translate(Vector3.right * h * speed * Time.fixedDeltaTime, Space.World);
//×上Time.deltaTime是以秒为单位移动
if (h < 0)
{
sr.sprite = tankSprites[3];
bulletEulerAngles = new Vector3(0, 0, 90);
}
else if (h > 0)
{
sr.sprite = tankSprites[1];
bulletEulerAngles = new Vector3(0, 0, -90);
}
if (Mathf.Abs(h) > 0.05f)
{
moveAudio.clip = tankAudio[1];
if (!moveAudio.isPlaying)
{
moveAudio.Play();
}
}
else
{
moveAudio.clip = tankAudio[0];
if (!moveAudio.isPlaying)
{
moveAudio.Play();
}
}
}
//坦克的死亡方法
public void Die()
{
if (!isDefended)
{
//玩家死亡标志位设置为true
PlayerManager.Instance.isDead = true;
//产生爆炸
Instantiate(explosionPrefab, transform.position, transform.rotation);
//死亡
Destroy(gameObject);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour {
//引用
public float speed = 10f;
public Sprite[] tankSprites;//上 右 下 左
public GameObject bullet;
public GameObject explosionPrefab;
//属性
private SpriteRenderer sr;
private Vector3 bulletEulerAngles;
private float h;
private float v=-1;
//计时器
private float timeVal;
private float timeValChangeDirection;
private void Awake()
{
sr = GetComponent<SpriteRenderer>();
}
void Start()
{
}
//不固定
void Update()
{
//攻击的时间间隔2秒一次
if (timeVal >= 2f)
{
Attack();
}
else
{
timeVal += Time.deltaTime;
}
}
//Update后执行,固定时间执行0.02s执行一次
private void FixedUpdate()
{
Move();
}
//坦克攻击
private void Attack()
{
//子弹产生的角度:当前坦克角度+子弹应该旋转的角度
Instantiate(bullet, transform.position, Quaternion.Euler(transform.eulerAngles + bulletEulerAngles));
timeVal = 0;
}
//坦克移动
private void Move()
{
if(timeValChangeDirection>=4)
{
int num = Random.Range(0, 8);
if(num>=5)// 5 6 7会向下走
{
v = -1;
h = 0;
}
else if(num==0)//0向上走
{
v = 1;
h = 0;
}
else if(num>0&&num<=2)//1,2向右走
{
h = -1;
v = 0;
}
else if(num>2&&num<=4)//3,4向左走
{
h = 1;
v = 0;
}
timeValChangeDirection = 0;
}
else
{
timeValChangeDirection += Time.deltaTime;
}
transform.Translate(Vector3.up * v * speed * Time.fixedDeltaTime, Space.World);
if (v < 0)
{
sr.sprite = tankSprites[2];
bulletEulerAngles = new Vector3(0, 0, -180);
}
else if (v > 0)
{
sr.sprite = tankSprites[0];
bulletEulerAngles = new Vector3(0, 0, 0);
}
if (v != 0)
{
return;//意味着 玩家按着上或者下,只能上下移动,而无法再会执行左右操作
}
transform.Translate(Vector3.right * h * speed * Time.fixedDeltaTime, Space.World);
//×上Time.deltaTime是以秒为单位移动
if (h < 0)
{
sr.sprite = tankSprites[3];
bulletEulerAngles = new Vector3(0, 0, 90);
}
else if (h > 0)
{
sr.sprite = tankSprites[1];
bulletEulerAngles = new Vector3(0, 0, -90);
}
}
//坦克的死亡方法
public void Die()
{
//产生爆炸
Instantiate(explosionPrefab, transform.position, transform.rotation);
//死亡
Destroy(gameObject);
}
//如果两个敌人碰到了就会相反移动
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.tag=="Enemy")
{
timeValChangeDirection = 4;
}
}
}
在生成地图后,需考虑层级问题,因为草是会遮挡坦克的,所以坦克的层级在1,草的层级在2,其他层级在0,层级指的是
坦克的子弹能够把土墙打烂之类的逻辑都是很简单的,一般都懂怎么做了,基本就以上说明