So erstellen Sie ein Schlangenspiel in Unity
In diesem Beitrag zeige ich, wie man ein klassisches Schlangenspiel in Unity erstellt.
Unity In diesem Tutorial verwendete Version: Unity 2018.3.0f2 (64-Bit)
Schritt 1: Erstellen Sie das Skript
Da es sich um einen "One Script Game" handelt, benötigt dieses Tutorial nur 1 Skript:
SC_SnakeGameGenerator.cs
//You are free to use this script in Free or Commercial projects
//sharpcoderblog.com @2019
using System.Collections.Generic;
using UnityEngine;
public class SC_SnakeGameGenerator : MonoBehaviour
{
//Game area resolution, the higher number means more blocks
public int areaResolution = 22;
//Snake movement speed
public float snakeSpeed = 10f;
//Main Camera
public Camera mainCamera;
//Materials
public Material groundMaterial;
public Material snakeMaterial;
public Material headMaterial;
public Material fruitMaterial;
//Grid system
Renderer[] gameBlocks;
//Snake coordenates
List<int> snakeCoordinates = new List<int>();
enum Direction { Up, Down, Left, Right };
Direction snakeDirection = Direction.Right;
float timeTmp = 0;
//Block where the fruit is placed
int fruitBlockIndex = -1;
//Total accumulated points
int totalPoints = 0;
//Game status
bool gameStarted = false;
bool gameOver = false;
//Camera scaling
Bounds targetBounds;
//Text styling
GUIStyle mainStyle = new GUIStyle();
// Start is called before the first frame update
void Start()
{
//Generate play area
gameBlocks = new Renderer[areaResolution * areaResolution];
for (int x = 0; x < areaResolution; x++)
{
for (int y = 0; y < areaResolution; y++)
{
GameObject quadPrimitive = GameObject.CreatePrimitive(PrimitiveType.Quad);
quadPrimitive.transform.position = new Vector3(x, 0, y);
Destroy(quadPrimitive.GetComponent<Collider>());
quadPrimitive.transform.localEulerAngles = new Vector3(90, 0, 0);
quadPrimitive.transform.SetParent(transform);
gameBlocks[(x * areaResolution) + y] = quadPrimitive.GetComponent<Renderer>();
targetBounds.Encapsulate(gameBlocks[(x * areaResolution) + y].bounds);
}
}
//Scale the MainCamera to fit the game blocks
mainCamera.transform.eulerAngles = new Vector3(90, 0, 0);
mainCamera.orthographic = true;
float screenRatio = (float)Screen.width / (float)Screen.height;
float targetRatio = targetBounds.size.x / targetBounds.size.y;
if (screenRatio >= targetRatio)
{
mainCamera.orthographicSize = targetBounds.size.y / 2;
}
else
{
float differenceInSize = targetRatio / screenRatio;
mainCamera.orthographicSize = targetBounds.size.y / 2 * differenceInSize;
}
mainCamera.transform.position = new Vector3(targetBounds.center.x, targetBounds.center.y + 1, targetBounds.center.z);
//Generate the Snake with 3 blocks
InitializeSnake();
ApplyMaterials();
mainStyle.fontSize = 24;
mainStyle.alignment = TextAnchor.MiddleCenter;
mainStyle.normal.textColor = Color.white;
}
void InitializeSnake()
{
snakeCoordinates.Clear();
int firstlock = Random.Range(0, areaResolution - 1) + (areaResolution * 3);
snakeCoordinates.Add(firstlock);
snakeCoordinates.Add(firstlock - areaResolution);
snakeCoordinates.Add(firstlock - (areaResolution * 2));
gameBlocks[snakeCoordinates[0]].transform.localEulerAngles = new Vector3(90, 90, 0);
fruitBlockIndex = -1;
timeTmp = 1;
snakeDirection = Direction.Right;
totalPoints = 0;
}
// Update is called once per frame
void Update()
{
if (!gameStarted)
{
if (Input.anyKeyDown)
{
gameStarted = true;
}
return;
}
if (gameOver)
{
//Flicker the snake blocks
if (timeTmp < 0.44f)
{
timeTmp += Time.deltaTime;
}
else
{
timeTmp = 0;
for (int i = 0; i < snakeCoordinates.Count; i++)
{
if (gameBlocks[snakeCoordinates[i]].sharedMaterial == groundMaterial)
{
gameBlocks[snakeCoordinates[i]].sharedMaterial = (i == 0 ? headMaterial : snakeMaterial);
}
else
{
gameBlocks[snakeCoordinates[i]].sharedMaterial = groundMaterial;
}
}
}
if (Input.GetKeyDown(KeyCode.Space))
{
InitializeSnake();
ApplyMaterials();
gameOver = false;
gameStarted = false;
}
}
else
{
if (timeTmp < 1)
{
timeTmp += Time.deltaTime * snakeSpeed;
}
else
{
timeTmp = 0;
if (snakeDirection == Direction.Right || snakeDirection == Direction.Left)
{
//Detect if the Snake hit the sides
if (snakeDirection == Direction.Left && snakeCoordinates[0] < areaResolution)
{
gameOver = true;
return;
}
else if (snakeDirection == Direction.Right && snakeCoordinates[0] >= (gameBlocks.Length - areaResolution))
{
gameOver = true;
return;
}
int newCoordinate = snakeCoordinates[0] + (snakeDirection == Direction.Left ? -areaResolution : areaResolution);
//Snake has ran into itself, game over
if (snakeCoordinates.Contains(newCoordinate))
{
gameOver = true;
return;
}
if (newCoordinate < gameBlocks.Length)
{
for (int i = snakeCoordinates.Count - 1; i > 0; i--)
{
snakeCoordinates[i] = snakeCoordinates[i - 1];
}
snakeCoordinates[0] = newCoordinate;
gameBlocks[snakeCoordinates[0]].transform.localEulerAngles = new Vector3(90, (snakeDirection == Direction.Left ? -90 : 90), 0);
}
}
else if (snakeDirection == Direction.Up || snakeDirection == Direction.Down)
{
//Detect if snake hits the top or bottom
if (snakeDirection == Direction.Up && (snakeCoordinates[0] + 1) % areaResolution == 0)
{
gameOver = true;
return;
}
else if (snakeDirection == Direction.Down && (snakeCoordinates[0] + 1) % areaResolution == 1)
{
gameOver = true;
return;
}
int newCoordinate = snakeCoordinates[0] + (snakeDirection == Direction.Down ? -1 : 1);
//Snake has ran into itself, game over
if (snakeCoordinates.Contains(newCoordinate))
{
gameOver = true;
return;
}
if (newCoordinate < gameBlocks.Length)
{
for (int i = snakeCoordinates.Count - 1; i > 0; i--)
{
snakeCoordinates[i] = snakeCoordinates[i - 1];
}
snakeCoordinates[0] = newCoordinate;
gameBlocks[snakeCoordinates[0]].transform.localEulerAngles = new Vector3(90, (snakeDirection == Direction.Down ? 180 : 0), 0);
}
}
ApplyMaterials();
}
if (Input.GetKeyDown(KeyCode.RightArrow))
{
int newCoordinate = snakeCoordinates[0] + areaResolution;
if (!ContainsCoordinate(newCoordinate))
{
snakeDirection = Direction.Right;
}
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
int newCoordinate = snakeCoordinates[0] - areaResolution;
if (!ContainsCoordinate(newCoordinate))
{
snakeDirection = Direction.Left;
}
}
if (Input.GetKeyDown(KeyCode.UpArrow))
{
int newCoordinate = snakeCoordinates[0] + 1;
if (!ContainsCoordinate(newCoordinate))
{
snakeDirection = Direction.Up;
}
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
int newCoordinate = snakeCoordinates[0] - 1;
if (!ContainsCoordinate(newCoordinate))
{
snakeDirection = Direction.Down;
}
}
}
if (fruitBlockIndex < 0)
{
//Place a fruit block
int indexTmp = Random.Range(0, gameBlocks.Length - 1);
//Check if the block is not occupied with a snake block
for (int i = 0; i < snakeCoordinates.Count; i++)
{
if (snakeCoordinates[i] == indexTmp)
{
indexTmp = -1;
break;
}
}
fruitBlockIndex = indexTmp;
}
}
void ApplyMaterials()
{
//Apply Snake material
for (int i = 0; i < gameBlocks.Length; i++)
{
gameBlocks[i].sharedMaterial = groundMaterial;
bool fruitPicked = false;
for (int a = 0; a < snakeCoordinates.Count; a++)
{
if (snakeCoordinates[a] == i)
{
gameBlocks[i].sharedMaterial = (a == 0 ? headMaterial : snakeMaterial);
}
if (snakeCoordinates[a] == fruitBlockIndex)
{
//Pick a fruit
fruitPicked = true;
}
}
if (fruitPicked)
{
fruitBlockIndex = -1;
//Add new block
int snakeBlockRotationY = (int)gameBlocks[snakeCoordinates[snakeCoordinates.Count - 1]].transform.localEulerAngles.y;
//print(snakeBlockRotationY);
if (snakeBlockRotationY == 270)
{
snakeCoordinates.Add(snakeCoordinates[snakeCoordinates.Count - 1] + areaResolution);
}
else if (snakeBlockRotationY == 90)
{
snakeCoordinates.Add(snakeCoordinates[snakeCoordinates.Count - 1] - areaResolution);
}
else if (snakeBlockRotationY == 0)
{
snakeCoordinates.Add(snakeCoordinates[snakeCoordinates.Count - 1] + 1);
}
else if (snakeBlockRotationY == 180)
{
snakeCoordinates.Add(snakeCoordinates[snakeCoordinates.Count - 1] - 1);
}
totalPoints++;
}
if (i == fruitBlockIndex)
{
gameBlocks[i].sharedMaterial = fruitMaterial;
gameBlocks[i].transform.localEulerAngles = new Vector3(90, 0, 0);
}
}
}
bool ContainsCoordinate(int coordinate)
{
for (int i = 0; i < snakeCoordinates.Count; i++)
{
if (snakeCoordinates[i] == coordinate)
{
return true;
}
}
return false;
}
void OnGUI()
{
//Display Player score and other info
if (gameStarted)
{
GUI.Label(new Rect(Screen.width / 2 - 100, 5, 200, 20), totalPoints.ToString(), mainStyle);
}
else
{
GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 10, 200, 20), "Press Any Key to Play\n(Use Arrows to Change Direction)", mainStyle);
}
if (gameOver)
{
GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 20, 200, 40), "Game Over\n(Press 'Space' to Restart)", mainStyle);
}
}
}
Das obige Skript erstellt ein Raster aus primitiven Quadern und ändert dann deren Materialien in eines der vier: ein Hintergrundmaterial, ein Schlangenkopfmaterial, ein Schlangenkörpermaterial oder ein Apfelmaterial. Außerdem wird die Kamera automatisch direkt über dem Rastersystem platziert und ihre orthografische Größe geändert, um die kollektiven Grenzen aller Blöcke einzuschließen.
Schritt 2: Richten Sie das Snake-Spiel ein
Jetzt richten wir das Snake-Spiel mit dem obigen Skript ein:
- Neue Szene erstellen
- Ändern Sie die Auflösung der Spielansicht, sodass Breite und Höhe gleich sind (z. B. 600 x 600 Pixel).
- Erstellen Sie ein neues GameObject (GameObject -> Create Empty) und benennen Sie es "_GameGenerator"
- Hängen Sie das SC_SnakeGameGenerator.cs-Skript an das _GameGenerator-Objekt an
Wie Sie feststellen werden, verfügt SC_SnakeGameGenerator über einige Variablen, die zugewiesen werden müssen:
- Die Variable Hauptkamera ist selbsterklärend. Weisen Sie die Standardhauptkamera zu.
- Erstellen Sie nun für die Materialien 4 Materialien (Rechtsklick -> Erstellen -> Material) und nennen Sie sie jeweils "ground_material", "snake_material", "head_material" und "fruit_material":
Ändern Sie für das ground_material den Shader in „Unlit/Color“ und ändern Sie die Hauptfarbe in „Schwarz“:
Für die anderen 3 Materialien ändern Sie den Shader auf „Unlit/Texture“ und weisen Sie die folgenden Texturen zu:
Für Snake_material:
Für head_material:
Für Obstmaterial:
- Ordnen Sie die Materialien den Variablen zu
Jetzt heißt es Play drücken und das Spiel testen:
Alles funktioniert wie erwartet, jetzt haben Sie ein spielbares Schlangenspiel in Unity.