Copy childhood FC classic game – tank war

I am participating in the nuggets Community game creative submission Contest. For details, please see: Game Creative Submission Contest

You must have played an FC game – Tank War when you were a child. My favorite game was this game when I was a child. Today, I will write a single-player version of tank War and try it online.

Development process:

  • Start interface
  • Randomly generated map
  • Create tank classes (handle tank sound, birth, death, fire, movement, etc.)
  • Enemy AI handling
  • End of game logic

1. Start screen

The logic of the start screen is simple, just add an option to select P1 and P2 and a start button, and listen for input.

public class Option : MonoBehaviour
{
    private int option = 0;
    public Transform pos1;
    public Transform pos2;

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.W))
        {
            transform.position = pos1.position;
            option = 0;
        }
        if (Input.GetKeyDown(KeyCode.S))
        {
            transform.position = pos2.position;
            option = 1;
        }
        if(option == 0 && Input.GetKeyDown(KeyCode.Space))
        {
            SceneManager.LoadScene(1); }}}Copy the code

2. Random map generation

  • Load and save each map block resource
  • Use random numbers to control random map block types
  • The for loop generates each segment of the map.
public class MapCreation : MonoBehaviour
{
    // 0: Old home 1: wall 2: Obstacle 3: Birth Effect 4: River 5: Grass 6: Air Wall
    public GameObject[] item;
    private Vector3 heartPosition;
    private Vector3[] defendHeartWallPosition = new Vector3[5];
    private List<Vector3> existPosition = new List<Vector3>();

    private void Awake()
    {
        CreateHeart();
        CreateAirBarriar();
        CreatePlayer();
        existPosition.Add(new Vector3(- 10.8.0));
        existPosition.Add(new Vector3(0.8.0));
        existPosition.Add(new Vector3(10.8.0));
        CreateRandomMap();
        InvokeRepeating("createEnemy".0.5);
    }

    private Vector3 createEnemyPosition()
    {
        int positionIndex = Random.Range(0.3);
        Vector3[] list = new Vector3[3] { new Vector3(- 10.8.0), new Vector3(10.8.0), new Vector3(0.8.0)};return list[positionIndex];
    }

    private void createEnemy()
    {
        if (PlayerManager.Instance.enemyNum <= 0)
        {
            return;
        }
        GameObject enemy = CreateItem(item[3], createEnemyPosition(), Quaternion.identity);
        enemy.GetComponent<Born>().createPlayer = false;
    }

    private GameObject CreateItem(GameObject createGameObject, Vector3 position, Quaternion rotation)
    {
        GameObject gb = Instantiate(createGameObject, position, rotation);
        gb.transform.SetParent(gameObject.transform);
        if(! HasPosition(position)) { existPosition.Add(position); }return gb;
    }

    private void CreateRandomMap()
    {
        for (int i = 0; i < 20; i++)
        {
            CreateItem(item[2], CreateRandomPosition(), Quaternion.identity);
            CreateItem(item[4], CreateRandomPosition(), Quaternion.identity);
            CreateItem(item[5], CreateRandomPosition(), Quaternion.identity);
        }
        for (int i = 0; i < 50; i++)
        {
            CreateItem(item[1], CreateRandomPosition(), Quaternion.identity); }}private void CreatePlayer()
    {
        CreateItem(item[3].new Vector3(2 -.- 8 -.0), Quaternion.identity);
    }

    private void CreateHeart()
    {
        // instantiate the old home
        heartPosition = new Vector3(0.- 8 -.0);
        defendHeartWallPosition[0] = new Vector3(- 1.- 8 -.0);
        defendHeartWallPosition[1] = new Vector3(1.- 8 -.0);
        for (int i = 2; i <= 4; i++)
        {
            defendHeartWallPosition[i] = new Vector3(i - 3.7 -.0);
        }
        CreateItem(item[0], heartPosition, Quaternion.identity);
        for (int i = 0; i < defendHeartWallPosition.Length; i++)
        {
            CreateItem(item[1], defendHeartWallPosition[i], Quaternion.identity); }}private void CreateAirBarriar()
    {
        for (int i = - 10; i < 11; i++)
        {
            CreateItem(item[6].new Vector3(i, 9.0), Quaternion.identity);
            CreateItem(item[6].new Vector3(i, 9 -.0), Quaternion.identity);
        }
        for (int i = - 8 -; i < 9; i++)
        {
            CreateItem(item[6].new Vector3(- 11, i, 0), Quaternion.identity);
            CreateItem(item[6].new Vector3(11, i, 0), Quaternion.identity); }}private Vector3 CreateRandomPosition()
    {
        while (true)
        {
            Vector3 newPosition = new Vector3(Random.Range(- 10.11), Random.Range(8.9 -), 0);
            if(! HasPosition(newPosition)) {returnnewPosition; }}}private bool HasPosition(Vector3 position)
    {
        for (int i = 0; i < existPosition.Count; i++)
        {
            if(existPosition[i] == position)
            {
                return true; }}return false; }}Copy the code

3. Player birth logic

This one is easier, initialize the player’s prefab and play the birth animation.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Born : MonoBehaviour
{
    public GameObject playerPrefab;
    public GameObject[] enemyPrefabs;
    public bool createPlayer;
    // Start is called before the first frame update
    void Start()
    {
        Invoke("PlayerBorn".0.8 f);
        Destroy(gameObject, 0.8 f);
    }

    private void PlayerBorn()
    {
        if(createPlayer)
        {
            Instantiate(playerPrefab, transform.position, transform.rotation);
        }
        else
        {
            int index = Random.Range(0.2); Instantiate(enemyPrefabs[index], transform.position, transform.rotation); }}}Copy the code

4. Create player classes and enemy classes

Here are some things to implement:

  • Tank movement, firing, death
  • Enemy Ai
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public float moveSpeed = 3;
    public Sprite[] tankSprite;
    public GameObject bulletPrefab;
    public GameObject explosionPrefab;
    public GameObject shieldPrefab;
    public AudioClip attackAudio;
    public AudioSource moveAudio;
    public AudioClip[] tankAudio;
    private SpriteRenderer sr;
    private Vector3 bulletEulerAngle;
    private float attackTime;
    private float isDefendedTimeVal = 3f;
    private bool isDefended = true;
     
    // Start is called before the first frame update
    private void Awake() 
    {
        sr = GetComponent<SpriteRenderer>();
    }

    void Start(){}// Update is called once per frame
    void Update()
    {
        if(isDefended)
        {
            shieldPrefab.SetActive(true);
            isDefendedTimeVal -= Time.deltaTime;
            if(isDefendedTimeVal <= 0)
            { 
                isDefended = false;
                shieldPrefab.SetActive(false); }}}private void FixedUpdate()
    {
        if(! PlayerManager.Instance.isDefeat) { Move();if (attackTime >= 0.4 f)
            {
                Attack();
            }
            else{ attackTime += Time.fixedDeltaTime; }}}private void Attack()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            AudioSource.PlayClipAtPoint(attackAudio, transform.position);
            Instantiate(bulletPrefab, transform.position, Quaternion.Euler(transform.eulerAngles + bulletEulerAngle));
            attackTime = 0; }}private void Move()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");
        if(v ! =0&& h ! =0)
        {
            return;
        }
        if (h > 0)
        {
            sr.sprite = tankSprite[1];
            bulletEulerAngle = new Vector3(0.0.- 90.);
        }
        else if (h < 0)
        {
            sr.sprite = tankSprite[3];
            bulletEulerAngle = new Vector3(0.0.90);
        }
        if (v > 0)
        {
            sr.sprite = tankSprite[0];
            bulletEulerAngle = new Vector3(0.0.0);
        }
        else if (v < 0)
        {
            sr.sprite = tankSprite[2];
            bulletEulerAngle = new Vector3(0.0.- 180.);
        }
        if (Mathf.Abs(v) > 0.05 f || Mathf.Abs(h) > 0.05 f)
        {
            moveAudio.clip = tankAudio[1];
            moveAudio.volume = 0.1 f;
            if (!moveAudio.isPlaying)
            {
                moveAudio.Play();
            }
        }
        else
        {
            moveAudio.clip = tankAudio[0];
            moveAudio.volume = 0.1 f;
            if(! moveAudio.isPlaying) { moveAudio.Play(); } } transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World); transform.Translate(Vector3.up * v * moveSpeed * Time.fixedDeltaTime, Space.World); }private void Die()
    {
        if(! isDefended) { Instantiate(explosionPrefab, transform.position, transform.rotation); Destroy(gameObject); PlayerManager.Instance.isDead =true; PlayerManager.Instance.life--; }}}Copy the code

The enemy:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public float moveSpeed = 3;
    public Sprite[] tankSprite;
    public GameObject bulletPrefab;
    public GameObject explosionPrefab;
    private SpriteRenderer sr;
    private Vector3 bulletEulerAngle;
    private float attackTime;
    private float turnDirectionTime;
    private float v;
    private float h;

    // Start is called before the first frame update
    private void Awake()
    {
        sr = GetComponent<SpriteRenderer>();
    }

    void Start(){}// Update is called once per frame
    void Update()
    {
        if (attackTime >= 3)
        {
            Attack();
        }
        else{ attackTime += Time.deltaTime; }}private void FixedUpdate()
    {
        Move();
    }

    private void Attack()
    {
        Instantiate(bulletPrefab, transform.position, Quaternion.Euler(transform.eulerAngles + bulletEulerAngle));
        attackTime = 0;
    }

    private void Move()
    {
        if (turnDirectionTime >= 4)
        {
            int randomDirection = Random.Range(0.8);
            if (randomDirection >= 5)
            {
                v = - 1;
                h = 0;
            }
            else if (randomDirection >= 3)
            {
                v = 0;
                h = 1;
            }
            else if (randomDirection >= 1)
            {
                v = 0;
                h = - 1;
            }
            else
            {
                v = - 1;
                h = 0;
            }
            turnDirectionTime = 0;
        }
        else
        {
            turnDirectionTime += Time.deltaTime + 0.05 f;
        }
        
        if (h > 0)
        {
            sr.sprite = tankSprite[1];
            bulletEulerAngle = new Vector3(0.0.- 90.);
        }
        else if (h < 0)
        {
            sr.sprite = tankSprite[3];
            bulletEulerAngle = new Vector3(0.0.90);
        }
        if (v > 0)
        {
            sr.sprite = tankSprite[0];
            bulletEulerAngle = new Vector3(0.0.0);
        }
        else if (v < 0)
        {
            sr.sprite = tankSprite[2];
            bulletEulerAngle = new Vector3(0.0.- 180.);
        }
        transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World);
        transform.Translate(Vector3.up * v * moveSpeed * Time.fixedDeltaTime, Space.World);
    }

    private void Die()
    {
        Instantiate(explosionPrefab, transform.position, transform.rotation);
        Destroy(gameObject);
    }
     
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if(collision.transform.tag == "Enemy")
        {
            turnDirectionTime = 5; }}}Copy the code

5. Handle bullet logic

This is where you deal with the sound and effects of bullets hitting bricks, tanks, and steel

public class Bullet : MonoBehaviour
{
    public float moveSpeed = 10;
    public bool isPlayerBullet;
    public AudioClip barriarAudio;

    void Update()
    {
        transform.Translate(transform.up * moveSpeed * Time.deltaTime, Space.World);
    }


    private void OnTriggerEnter2D(Collider2D collision)
    {
        switch (collision.tag)
        {
            case "Tank":
                if(! isPlayerBullet) { collision.SendMessage("Die");
                    Destroy(gameObject);
                }
                break;
            case "Heart":
                collision.SendMessage("Die");
                Destroy(gameObject);
                break;
            case "Wall":
                Destroy(collision.gameObject);
                Destroy(gameObject);
                break;
            case "Enemy":
                if (isPlayerBullet)
                {
                    collision.SendMessage("Die");
                    Destroy(gameObject);
                    PlayerManager.Instance.score++;
                    PlayerManager.Instance.enemyNum--;
                }
                break;
            case "Barriar":
                if(isPlayerBullet)
                {
                    AudioSource.PlayClipAtPoint(barriarAudio, transform.position);
                }
                Destroy(gameObject);
                break;
            default:
                break; }}}Copy the code

6. Generate hometowns and handle game end logic

The game ends when the home is attacked

public class Heart : MonoBehaviour
{
    private SpriteRenderer sr;
    public Sprite brokenHeart;
    public GameObject explosionPrefab;
    public AudioClip dieAudio;
    // Start is called before the first frame update
    void Start()
    {
        sr = GetComponent<SpriteRenderer>();
    }

    private void Die()
    {
        AudioSource.PlayClipAtPoint(dieAudio, transform.position);
        Instantiate(explosionPrefab, transform.position, transform.rotation);
        sr.sprite = brokenHeart;
        PlayerManager.Instance.isDefeat = true; }}Copy the code

The code is relatively simple, to achieve the most basic game logic, you can expand some tank weapons, such as rocket launcher, mines and other game props, to create your own tank war.