Ping pong

Yleistä

  1. Aloita uusi projekti, nimeä esimerkiksi PingPong, muista valita Core Universal 2D-projekti.
  2. Lisää Assets / Scenes-kansioon uusi Scene, nimeä se nimellä Game. Voit poistaa SampleScene-objektin.
  3. Lisää Hierarchy-ikkunassa uusi tyhjä GameObject, nimeä ne GameManager.
  4. Lisää uusi kansio Scripts, lisää tänne C#-skirptit GameManager.cs, Ball.cs ja Paddle.cs (tarkenteita .cs ei tarvitse kirjoittaa). Voit avata nämä tuplaklikkaamalla ja varmistaa, että kaikki aukeavat oikein. Tarkista, että luokkien (class) nimi on GameManager, Ball ja Paddle.
  5. Lisää uusi kansio Sprites ja lisää tänne ympyrä (Assets / Create / Sprites / Circle) ja neliö (Assets / Create / Sprites / Square).
  6. Raahaa ympyrä Hierarchy-ikkunaan ja tehdään siitä näin peliin oma objekti. Nimeä ympyrä palloksi (Ball).

Collider

  1. Lisää Ball-objektille Inspector-ikkunassa uusi komponentti, Circle Collider 2D (Component / Physics 2D / Circle Collider 2D). Valitse törmäyskomponentille Is Trigger -valinta päälle.
  2. Lisää Ball-objektille myös Rigidbody 2D-komponentti (Component / Physics 2D / Rigidbody 2D). Valitse Body Type-kohdassa Kinematic.
  3. Tehdään seuraavaksi maila (Paddle): raahaa Square Hierarchy-ikkunaan ja nimeä objekti nimellä Paddle. Raahaa neliö vähän oikealle ja muokkaa sen kokoa jotta se muistuttaisi mailaa.
  4. Lisää Paddle-objektille Rigidbody 2D, aseta Body Type Kinematic. Lisää myös Box Collider 2D, aseta Is Trigger päälle.

  5. Lisätään mailalle tagi jotta myöhemmin tiedämme ohjata mailaa koodissa: Inspector-ikkunassa valitse Tag ja Add Tag, anna uudeksi tagiksi "Paddle". Klikkaa uudestaan ja varmista että Tag-kohdassa on nyt Paddle.

  6. Lisää pallolle Tagiksi "Ball".

Skriptin aloittaminen

  1. Valitse GameManager Hierarchy-ikkunassa. Raahaa GameManager-skripti uudeksi komponentiksi.
  2. Lisätään vähän koodia:
    public class GameManager : MonoBehaviour
    {
        public Ball ball;
        public Paddle paddle;
    
    Tarkista näkyvätkö nyt muuttujat oikein GameManager-objektilla.
  3. Seuraavaksi liitetään Ball.cs -skripti Ball-objektille ja Paddle.cs-skripti Paddle-objektille. Valitse Ball ja raahaa skripti sille. Toista Paddle-objektille.
  4. Prefab: tee uusi kansio Prefabs. Prefab on uudelleenkäytettävä valmis olio. Tehdään Game Objecteista Ball ja Paddle tällaisia Prefabeja raahaamalla ne tähän kansioon.
  5. Tämän jälkeen voit poistaa Ball ja Paddle-oliot Hierarchy-ikkunasta (voimme luoda nyt nämä oliot koodissa).
  6. Raahaa Ball-prefab ja Paddle-prefab GameManagerin vastaaville muuttujille.
  7. Lisätään pelin aloittaminen GameManager.cs-skriptin Start()-metodiin:
    void Start()
    {
    	Instantiate(ball);
    	Instantiate(paddle);
    }
    

    Testaa ajaa ohjelma. Näkyykö näytöllä nyt pallo ja maila.

Maila

  1. Paddle.cs -skriptille pitää seuraavaksi lisätä Init()-metodi jossa alustetaan oikean tai vasemman mailan sijainti:
    public class Paddle : MonoBehaviour
    {
    
        public void Init(bool isRightPaddle) {
            Vector2 pos = Vector2.zero;
    
            if (isRightPaddle)
            {
                // maila oikealle
                pos = new Vector2 (GameManager.topRight.x, 0);
            }
            else
            {
                // maila vasemmalle:
                pos = new Vector2(GameManager.bottomLeft.x, 0);
            }
            // muuttaa sijainnin uudeksi:
            transform.position = pos;
        }
    
    
    }
    
  2. Siten GameManager-luokassa luodaan kaksi mailaa ja määritellään pelialueen vasen alareuna ja oikea yläreuna.
    public class GameManager : MonoBehaviour
    {
        // esitellään julkiset muuttujat
        public Ball ball;
        public Paddle paddle;
    
        public static Vector2 bottomLeft;
        public static Vector2 topRight;
    
        // Start is called before the first frame update
        void Start()
        {
            bottomLeft = Camera.main.ScreenToWorldPoint(new Vector2(0, 0));
            topRight = Camera.main.ScreenToWorldPoint(new Vector2(Screen.width, Screen.height));
    
            // luodaan pallo
            Instantiate(ball);
    
            // luodaan kaksi mailaa
            Paddle paddle1 = Instantiate(paddle) as Paddle;
            Paddle paddle2 = Instantiate(paddle) as Paddle;
            // lisätään mailalle kohta boolean-arvo
            // joka kertoo onko se oikea vai vasen
            paddle1.Init(true);
            paddle2.Init(false);
        }
    
    }
    
  3. Aja ohjelma ja katso miltä sovellus näyttää. Mailat ovat ihan reunassa, jotta ne saadaan vähän keskemmälle meidän pitää muuttaa sijaintia:
    if (isRightPaddle)
     {
      // maila oikealle
      pos = new Vector2 (GameManager.topRight.x, 0);
      pos -= Vector2.right * transform.localScale.x;
     }
    

    Tee vastaava asia myös else-lauseessa.

  4. Seuraavaksi yritetään liikuttaa mailaa ylös ja alas. Tarvitaan muuttujia mailalle:
    public class Paddle : MonoBehaviour
    {
    private float speed;
    private float height;
    private string input;
    public bool isRight;	
    

    Start()-metodissa alustetaan korkeus ja nopeus.

    void Start()
    {
    height = transform.localScale.y;
    //speed = 5f;
    }	
    

    Lopuksi alustetaan isRight Init()-metodissa:

    isRight = isRightPaddle;
    
  5. Seuraavaksi määritellään input-syöte. Tämä tapahtuu Edit / Project Settings-valikosta. Valitse kohta Input manager ja valmiiksi määritelty Horizontal . Muuta Size -> 2. Muuta sitten nimi Horizontal -> PaddleLeft. Muuta Vertical -> PaddleRight. Asetetaan lisäksi näille oikeat painikkeet, aseta vasemmalle esimerkiksi 'w' ja 's', ota Alt Negative ja Alt Positive pois käytöstä. Toista oikealle mailalle.

  6. Meidän pitää kertoa seuraavaksi mailalle kumpi on oikea input-muuttujan arvo. Jos maila on oikea niin asetetaan input = "PaddleRight";, muuten asetetaan input = "PaddleLeft";.
     if (isRightPaddle)
    {
    	input = "PaddleRight";
    	// maila oikealle
    	pos = new Vector2(GameManager.topRight.x, 0);
    	pos -= Vector2.right * transform.localScale.x;
    	// vasemmalla: pos -= Vector2.left * transform.localScale.x;
    }
    

    Kokeile toimintaa, tarvitaan myös vasen maila eli lisää else-lause jossa asetetaan vasemman mailan nimi.

  7. Aseta myös Game Objektin nimeksi input-muuttujan arvo (Paddle.cs, Init()-metodi):

    // muuttaa sijainnin uudeksi:
    transform.position = pos;
    transform.name = input;
    

    Voit ajaa ohjelman ja tarkistaa että Hieararchy-ikkunaan ilmestyy oikealla nimellä kaksi mailaa.

  8. Paddle.cs -tiedoston Update()-metodissa muutetaan objektin sijaintia seuraavasti:
    void Update()
    {
    float move = Input.GetAxis(input) * Time.deltaTime * speed;
    transform.Translate (move * Vector2.up);
    }
  9. Muuta mailan speed-attribuutti seuraavasti:
    [SerializeField]
    private float speed;	
    

    Nyt speed-arvoa voidaan muuttaa editorissa suoraan käsin. Kokeile muuttaa kentän arvoa (esim. 15) editorissa ja testaa toimintaa.

  10. Mailan liikkeen estäminen ruudun ulkopuolelle. Update() -metodissa ennen mailan liikuttamista tarkistetaan pitääkö liike pysäyttää:
    float move = Input.GetAxis(input) * Time.deltaTime * speed;
    // estetään mailan meneminen ruudun alle
    // jos liike on alas ja y on tiettyä arvoa pienempi
    if (transform.position.y < GameManager.bottomLeft.y + height / 2 && move < 0) {
    move = 0;
    }
    else if (transform.position.y > GameManager.topRight.y - height / 2 && move > 0) {
    move = 0;
    }
    transform.Translate(move * Vector2.up);
    

Pallo

  1. Pallon liikuttaminen menee suurin piirtein samaan tapaan kuin mailakin:
    public class Ball : MonoBehaviour
    {
    	[SerializeField]
    	private float speed;
    	private float radius;
    	private Vector2 direction;	
    

    Esittele muuttujat ja anmna speed-muuttujalle arvo editorissa.

  2. Start()-metodissa alustetaan muuttuajt:
    void Start()
    {
    // aloitussuunta suoraan
    direction = Vector2.one.normalized;    
    radius = transform.localScale.x / 2;
    }
  3. Update()-metodissa hoidetaan pallon liike. Tulee
    void Update()
    {
    // liikutetaan palloa
    transform.Translate (direction * speed * Time.deltaTime);
    if (transform.position.y < GameManager.bottomLeft.y + radius && direction.y < 0)
    {
    direction.y = -direction.y;
    }
    if (transform.position.y > GameManager.topRight.y - radius && direction.y > 0)
    {
    direction.y = -direction.y;
    }
    

    Lisäksi pitää tarkistaa voittiko jompi kumpi pelaaja:

    // tarkistetaan voittiko pelaaja
    if (transform.position.x < GameManager.bottomLeft.x + radius && direction.x < 0)
    {
    Debug.Log("Oikea pelaaja voitti!");
    }
    else if (transform.position.x > GameManager.topRight.x - radius && direction.x > 0)
    {
    Debug.Log("Vasen pelaaja voitti!");
    }
    
  4. Viimeinen asia on mailan osuminen palloon. Kun maila osuu niin vaihdetaan suunta päinvastaiseen. Lisää koodi Ball.cs-tiedostoosi:
    void OnTriggerEnter2D(Collider2D other) {
    	if (other.tag == "Paddle") {
    		// tarkistetaan onko maila oikea vai vasen
    		bool isRight = other.GetComponent<Paddle>().isRight;
    		if (isRight == true && direction.x > 0) {
    			direction.x = -direction.x;
    		}
    		if (isRight == false && direction.x < 0) {
    			direction.x = -direction.x;
    		}
    	}
    }	
    

Lisäyksiä

  • Kun peli päättyy niin pysäytä aika pallon Update()-metodissa:
    Time.timeScale = 0;
    // lopetetaan skriptin suoritus
    enabled = false; 
    
  • Kun pallo osuu mailaan niin kasvata speed-ominaisuutta yhdellä.
  • Ääniefektien lisääminen. Etsi sopiva ääni pallon osumiseen ja toinen pelin voittamiseen.
    • Lisää Assets-kansion alle alikansio Sounds.
    • Etsi sopiva ilmainen ääniefekti, raahaa / kopioi se Sounds-kansioosi.
    • Lisää pallolle uusi komponentti, Audio Source. Valitse AudioClip-ominaisuudeksi oma äänitiedostosi.
    • Lisää koodi dokumentaation mukaan.

Lähteitä ja linkkejä