Posted on

How to make Pong in Unity Lesson 3

Play Classic Pong

Now we want to create a prefab for the ball object of Pong, which is the other major piece of the game besides the paddle. To do this we will begin with the ball controller script. Go ahead and create a new C# script in your scripts folder and call it BallController. Then double click on it to open it up in your coding environment.

The first thing we will do inside this script is to create some new variables.

Rigidbody2D myRb;
bool setSpeed;
[SerializeField] float speedUp;
float xSpeed;
float ySpeed;

Now, we need to initialize our Rigidbody2D variable and we can do this within the Start function. Next, let us create a new function that we will use to move the ball. This will be a void function called MoveBall with no

Developer’s Insight: We need to think about how we want to move the ball. We are already moving the paddles with the Translate function and if we move the ball in the same way then there will be no physics interactions that we need in order to trigger different events. Because of this, it would be best to move the ball using the velocity variable of the Rigidbody component.

using Photon.Chat;
using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PhotonChatManager : MonoBehaviour, IChatClientListener
{
    #region Setup

    [SerializeField] GameObject joinChatButton;
    ChatClient chatClient;
    bool isConnected;
    [SerializeField] string username;

    public void UsernameOnValueChange(string valueIn)
    {
        username = valueIn;
    }

    public void ChatConnectOnClick()
    {
        isConnected = true;
        chatClient = new ChatClient(this);
        //chatClient.ChatRegion = "US";
        chatClient.Connect(PhotonNetwork.PhotonServerSettings.AppSettings.AppIdChat, PhotonNetwork.AppVersion, new AuthenticationValues(username));
        Debug.Log("Connenting");
    }

    #endregion Setup

    #region General

    [SerializeField] GameObject chatPanel;
    string privateReceiver = "";
    string currentChat;
    [SerializeField] InputField chatField;
    [SerializeField] Text chatDisplay;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        if (isConnected)
        {
            chatClient.Service();
        }

        if (chatField.text != "" && Input.GetKey(KeyCode.Return))
        {
            SubmitPublicChatOnClick();
            SubmitPrivateChatOnClick();
        }
    }

    #endregion General

    #region PublicChat

    public void SubmitPublicChatOnClick()
    {
        if (privateReceiver == "")
        {
            chatClient.PublishMessage("RegionChannel", currentChat);
            chatField.text = "";
            currentChat = "";
        }
    }

    public void TypeChatOnValueChange(string valueIn)
    {
        currentChat = valueIn;
    }

    #endregion PublicChat

    #region PrivateChat

    public void ReceiverOnValueChange(string valueIn)
    {
        privateReceiver = valueIn;
    }

    public void SubmitPrivateChatOnClick()
    {
        if (privateReceiver != "")
        {
            chatClient.SendPrivateMessage(privateReceiver, currentChat);
            chatField.text = "";
            currentChat = "";
        }
    }

    #endregion PrivateChat

    #region Callbacks

    public void DebugReturn(DebugLevel level, string message)
    {
        //throw new System.NotImplementedException();
    }

    public void OnChatStateChange(ChatState state)
    {
        if(state == ChatState.Uninitialized)
        {
            isConnected = false;
            joinChatButton.SetActive(true);
            chatPanel.SetActive(false);
        }
    }

    public void OnConnected()
    {
        Debug.Log("Connected");
        joinChatButton.SetActive(false);
        chatClient.Subscribe(new string[] { "RegionChannel" });
    }

    public void OnDisconnected()
    {
        isConnected = false;
        joinChatButton.SetActive(true);
        chatPanel.SetActive(false);
    }

    public void OnGetMessages(string channelName, string[] senders, object[] messages)
    {
        string msgs = "";
        for (int i = 0; i < senders.Length; i++)
        {
            msgs = string.Format("{0}: {1}", senders[i], messages[i]);

            chatDisplay.text += "\n" + msgs;

            Debug.Log(msgs);
        }

    }

    public void OnPrivateMessage(string sender, object message, string channelName)
    {
        string msgs = "";

        msgs = string.Format("(Private) {0}: {1}", sender, message);

        chatDisplay.text += "\n " + msgs;

        Debug.Log(msgs);
        
    }

    public void OnStatusUpdate(string user, int status, bool gotMessage, object message)
    {
        throw new System.NotImplementedException();
    }

    public void OnSubscribed(string[] channels, bool[] results)
    {
        chatPanel.SetActive(true);
    }

    public void OnUnsubscribed(string[] channels)
    {
        throw new System.NotImplementedException();
    }

    public void OnUserSubscribed(string channel, string user)
    {
        throw new System.NotImplementedException();
    }

    public void OnUserUnsubscribed(string channel, string user)
    {
        throw new System.NotImplementedException();
    }

    #endregion Callbacks
}

Inside this function, we want to set the velocity the balls Rigidbody using a new Vector2 value of our xSpeed and ySpeed variables. We then want to call this function with our Update function.

But before we call the MoveBall function in the Update function we need to add some code to set the speed of the ball. This can be done by first, checking to see if the setSpeed variable equals false. If it is we then want to set it to true and set the xSpeed and ySpeed variables equal to a random range between the same number negative and positive.

Now we need to code for the different physics interactions our ball can have. The first will be for when it collides with another 2D collider. This is done by adding the special OnCollisionEnter2D function. Inside this function, we need to first hand the condition for if our ball collides with a wall object. Inside this condition, we must simple inverse the value of our xSpeed variable by multiplying it by -1. We then need to handle the condition for if our ball collides with a paddle. Inside this, if statement we must first invert the ySpeed variable by doing the same as we did with the xSpeed variable above. Then depending on the direction of the ball, we need to add or subtract the speedUp value to the ySpeed and xSpeed variables. If the speed is positive then you want to add. If the speed is negative then subtract.

Here is our version of this script so far.

using UnityEngine;
using System.Collections;

public class BallController : MonoBehaviour {

    Rigidbody2D myRb;
    bool setSpeed;
	[SerializeField] float speedUp;
	float xSpeed;
	float ySpeed;

	void Start()
	{
		myRb = GetComponent<Rigidbody2D>();
	}
	
	void Update () {
		
            if(!setSpeed)
            {
                setSpeed = true;
                
                xSpeed = Random.Range(1f, 2f) * Random.Range(0, 2) * 2 - 1;
                ySpeed = Random.Range(1f, 2f) * Random.Range(0, 2) * 2 - 1;
            }
			MoveBall();
		
	}

    void MoveBall()
    {
        myRb.velocity = new Vector2(xSpeed, ySpeed);
    }

	void OnCollisionEnter2D(Collision2D other)
	{
		
		if(other.transform.tag =="Wall")
		{
			xSpeed = xSpeed*-1;
		}
		
        if (other.transform.tag == "Paddle" )
        {
            ySpeed = ySpeed * -1;

            if(ySpeed > 0)
            {
                ySpeed += speedUp;
            }
            else
            {
                ySpeed -= speedUp;
            }
            if (xSpeed > 0)
            {
                xSpeed += speedUp;
            }
            else
            {
                xSpeed -= speedUp;
            }
        }

	}
}
using Photon.Chat;
using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PhotonChatManager : MonoBehaviour, IChatClientListener
{
    #region Setup

    [SerializeField] GameObject joinChatButton;
    ChatClient chatClient;
    bool isConnected;
    [SerializeField] string username;

    public void UsernameOnValueChange(string valueIn)
    {
        username = valueIn;
    }

    public void ChatConnectOnClick()
    {
        isConnected = true;
        chatClient = new ChatClient(this);
        //chatClient.ChatRegion = "US";
        chatClient.Connect(PhotonNetwork.PhotonServerSettings.AppSettings.AppIdChat, PhotonNetwork.AppVersion, new AuthenticationValues(username));
        Debug.Log("Connenting");
    }

    #endregion Setup

    #region General

    [SerializeField] GameObject chatPanel;
    string privateReceiver = "";
    string currentChat;
    [SerializeField] InputField chatField;
    [SerializeField] Text chatDisplay;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        if (isConnected)
        {
            chatClient.Service();
        }

        if (chatField.text != "" &amp;&amp; Input.GetKey(KeyCode.Return))
        {
            SubmitPublicChatOnClick();
            SubmitPrivateChatOnClick();
        }
    }

    #endregion General

    #region PublicChat

    public void SubmitPublicChatOnClick()
    {
        if (privateReceiver == "")
        {
            chatClient.PublishMessage("RegionChannel", currentChat);
            chatField.text = "";
            currentChat = "";
        }
    }

    public void TypeChatOnValueChange(string valueIn)
    {
        currentChat = valueIn;
    }

    #endregion PublicChat

    #region PrivateChat

    public void ReceiverOnValueChange(string valueIn)
    {
        privateReceiver = valueIn;
    }

    public void SubmitPrivateChatOnClick()
    {
        if (privateReceiver != "")
        {
            chatClient.SendPrivateMessage(privateReceiver, currentChat);
            chatField.text = "";
            currentChat = "";
        }
    }

    #endregion PrivateChat

    #region Callbacks

    public void DebugReturn(DebugLevel level, string message)
    {
        //throw new System.NotImplementedException();
    }

    public void OnChatStateChange(ChatState state)
    {
        if(state == ChatState.Uninitialized)
        {
            isConnected = false;
            joinChatButton.SetActive(true);
            chatPanel.SetActive(false);
        }
    }

    public void OnConnected()
    {
        Debug.Log("Connected");
        joinChatButton.SetActive(false);
        chatClient.Subscribe(new string[] { "RegionChannel" });
    }

    public void OnDisconnected()
    {
        isConnected = false;
        joinChatButton.SetActive(true);
        chatPanel.SetActive(false);
    }

    public void OnGetMessages(string channelName, string[] senders, object[] messages)
    {
        string msgs = "";
        for (int i = 0; i &lt; senders.Length; i++)
        {
            msgs = string.Format("{0}: {1}", senders[i], messages[i]);

            chatDisplay.text += "\n" + msgs;

            Debug.Log(msgs);
        }

    }

    public void OnPrivateMessage(string sender, object message, string channelName)
    {
        string msgs = "";

        msgs = string.Format("(Private) {0}: {1}", sender, message);

        chatDisplay.text += "\n " + msgs;

        Debug.Log(msgs);
        
    }

    public void OnStatusUpdate(string user, int status, bool gotMessage, object message)
    {
        throw new System.NotImplementedException();
    }

    public void OnSubscribed(string[] channels, bool[] results)
    {
        chatPanel.SetActive(true);
    }

    public void OnUnsubscribed(string[] channels)
    {
        throw new System.NotImplementedException();
    }

    public void OnUserSubscribed(string channel, string user)
    {
        throw new System.NotImplementedException();
    }

    public void OnUserUnsubscribed(string channel, string user)
    {
        throw new System.NotImplementedException();
    }

    #endregion Callbacks
}

There is another physics interaction that we will need to code for the ball but we first need to code a different script so for now we will save this script and revisit it later. Go ahead and return to Unity. Inside Unity Create the following object.

  • : This is a 2D sprite object with the ball image as the sprite. It also has a Circle Collider 2D component, a Rigidbody 2D and our BallController script.

Once you have created this object in your scene, go ahead and turn it into a prefab by dragging it from your hierarchy into your Prefabs folder. Now with the ball and paddles still in your scene, you can test your game by clicking the play button. The ball should start moving immediately and If it hits the walls or you paddles it should “bounce” off.

Creative Thought: You could find a way to spawn more balls into your game making it harder for the players to protect their endzone.