The second game project at Futuregames. Our brief was to create an 'Extreme Sport' game on mainly a mobile device, using Unity.
I created and facilitated the ideation process. I used a 'Brainstorm' approach to what idea we should choose and then we voted on the sport we wanted the most.
I lead us through the building of the game concept with the use of our Miro board.
In the first game project at Futuregames, we had everybody on-site for the ideation. However, for this project, we had several people that were at other locations. Facilitating an online ideation session was challenging, as it was harder to involve everyone completely in the process, but I managed to keep the remote members engaged by actively seeking their input.
In the last project, I was in a leadership role for the latter part of the project, so this time I wanted a more hands-on role when it came to programming.
Network - I created the backbone of the network code, with the use of Photon, for connecting to other players and playing with each other.
UI Code and Design - I coded the base menus, the lobby system and the 'room' system. I also helped out with the final UI design.
Once we had left the prototype phase, our Lead Programmer stepped down and I was asked by the Product Owner to take over as the new Lead.
using UnityEngine;
using Photon.Pun;
public class PlayerRespawnTempPierre: MonoBehaviour
{
public string respawnPointTag = "Respawn";
public float respawnDelay = 0.1f;
private bool isRespawning = false;
private void OnCollisionEnter(Collision collision)
{
isRespawning = true;
Invoke("RespawnPlayer", respawnDelay);
}
private void RespawnPlayer()
{
isRespawning = false;
Vector3 respawnPosition = GetRandomRespawnPosition();
PhotonView pv = GetComponent<PhotonView>();
if (pv != null && pv.IsMine)
{
pv.RPC("TeleportTo", RpcTarget.All, respawnPosition);
}
}
private Vector3 GetRandomRespawnPosition()
{
GameObject[] respawnPoints = GameObject.FindGameObjectsWithTag(respawnPointTag);
if (respawnPoints.Length > 0)
{
int randomIndex = Random.Range(0, respawnPoints.Length);
return respawnPoints[randomIndex].transform.position;
}
// Return default position if no respawn points are found
return Vector3.zero;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Photon.Pun;
using Photon.Realtime;
using Photon.Pun.Demo.Cockpit;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Menu : MonoBehaviourPunCallbacks
{
public GameObject loadingScreen;
public GameObject connectingText;
public GameObject connectedScreen;
public GameObject lobbyRoom;
public GameObject panelRoom;
public GameObject startButton;
public InputField createInput;
public InputField joinInput;
public TMP_InputField usernameInput;
public RoomList activeRoomList;
private Text roomName;
public PhotonView PV;
#region Connecting to Server
private void Start()
{
PV = GetComponent<PhotonView>();
if (PhotonNetwork.IsConnected)
{
loadingScreen.SetActive(false);
PhotonNetwork.Disconnect();
PhotonNetwork.ConnectUsingSettings();
PhotonNetwork.AutomaticallySyncScene = true;
}
}
public void Connecting()
{
if (usernameInput.text.Length >= 1)
{
PhotonNetwork.NickName = usernameInput.text;
PhotonNetwork.AutomaticallySyncScene = true;
}
}
public override void OnConnectedToMaster()
{
PhotonNetwork.JoinLobby();
}
#endregion
#region CreateOrJoinRooms
public void CreateRoom()
{
if (createInput.text.Length >= 1)
{
RoomOptions roomOptions = new RoomOptions();
PhotonNetwork.CreateRoom(createInput.text, roomOptions);
SceneManager.LoadScene(1);
}
}
public override void OnCreatedRoom()
{
base.OnCreatedRoom();
// Check if the local player is the master client after room creation
if (PhotonNetwork.IsMasterClient)
{
startButton.SetActive(true);
}
}
public void JoinRoom()
{
PhotonNetwork.JoinRoom(joinInput.text);
}
public override void OnJoinedRoom()
{
lobbyRoom.SetActive(true);
connectedScreen.SetActive(false);
if (!PhotonNetwork.IsMasterClient)
{
startButton.SetActive(false);
}
}
#endregion
#region InsideRoom
public void StartGame()
{
if (PhotonNetwork.IsMasterClient)
{
PhotonNetwork.CurrentRoom.IsOpen = false;
PhotonNetwork.CurrentRoom.IsVisible = false;
PhotonNetwork.LoadLevel(1);
}
}
public void LeaveRoom()
{
if (PhotonNetwork.IsMasterClient)
{
PhotonNetwork.CurrentRoom.IsOpen = false;
PhotonNetwork.CurrentRoom.IsVisible = false;
PhotonNetwork.CurrentRoom.PlayerTtl = 0;
PhotonNetwork.CurrentRoom.EmptyRoomTtl = 0;
PV.RPC("RPC_HostLeaving", RpcTarget.AllBuffered);
lobbyRoom.SetActive(false);
}
else
{
PhotonNetwork.LeaveRoom();
lobbyRoom.SetActive(false);
}
}
[PunRPC]
void RPC_HostLeaving()
{
PhotonNetwork.LeaveRoom();
lobbyRoom.SetActive(false);
}
#endregion
}
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
public class RoomList : MonoBehaviourPunCallbacks
{
public GameObject roomPrefab;
public List<GameObject> _instantiatedLobbies = new List<GameObject>();
public List<RoomInfo> activeRooms = new();
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
ClearList();
// Instantiate new lobbies
foreach (var room in roomList)
{
if(room.PlayerCount == 0) continue;
activeRooms.Add(room);
GameObject Room = Instantiate(roomPrefab, Vector3.zero, Quaternion.identity, GameObject.Find("Content").transform);
Room thisRoom = Room.GetComponent<Room>();
thisRoom.Name.text = room.Name;
thisRoom.PlayerCount.text = room.PlayerCount + "/" + room.MaxPlayers;
_instantiatedLobbies.Add(Room);
}
}
public override void OnJoinedRoom()
{
base.OnJoinedRoom();
ClearList();
}
private void ClearList()
{
foreach (var lobby in _instantiatedLobbies)
{
Destroy(lobby);
}
_instantiatedLobbies.Clear();
activeRooms.Clear();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Photon.Pun;
using UnityEngine.SceneManagement;
using TMPro;
public class Room : MonoBehaviourPunCallbacks
{
public static Room Instance;
public TextMeshProUGUI Name;
public TextMeshProUGUI PlayerCount;
public List<SelectMap> allMaps = new List<SelectMap>();
public PhotonView PV;
private void Awake()
{
if(Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
}
}
public void JoinRoom()
{
GameObject.Find("MenuManager").GetComponent<Menu>().JoinRoomInList(Name.text);
}
[ReadOnly] public int LevelToLoad;
public void StartGame()
{
if (PhotonNetwork.IsMasterClient)
{
PhotonNetwork.CurrentRoom.IsOpen = false;
PhotonNetwork.CurrentRoom.IsVisible = false;
PhotonNetwork.LoadLevel(LevelToLoad);
}
}
public void LeaveRoom()
{
if (PhotonNetwork.IsMasterClient)
{
PhotonNetwork.CurrentRoom.IsOpen = false;
PhotonNetwork.CurrentRoom.IsVisible = false;
PhotonNetwork.CurrentRoom.PlayerTtl = 0;
PhotonNetwork.CurrentRoom.EmptyRoomTtl = 0;
PV.RPC("RPC_HostLeaving", RpcTarget.AllBuffered);
}
else
{
PhotonNetwork.LeaveRoom();
SceneManager.LoadScene(0);
}
}
[PunRPC]
void RPC_HostLeaving()
{
PhotonNetwork.LeaveRoom();
}
public override void OnLeftRoom()
{
// Now safe to load another level or perform cleanup.
PhotonNetwork.LoadLevel(0);
}
}
My tasks as a Lead were very different to my prior roles as a Programmer. Instead of focusing on the code itself, I was more as a support for the other programmers, the Product Owner and the designers.
Perforce - I was responsible for our Perforce deposit.
Scrum Master - Our project lacked a Scrum Master, so the Leads were assigned to Scrum for each discipline. We had a programmer working off-site, which I was in continous contact with him to make sure he was kept in the loop.
Technical Support - Our project was plagued with a lot of technical difficulties. I was the one who helped everyone with technical issues such as problems in Unity and Perforce.
QA - We had a QA person assigned to our group. I was in constant contact with him to get feedback that we needed and returned it back to the team.
Build Responsible - I was making builds, mostly daily, to distribute to our QA and to the rest of our team for testing purposes. I organized several playtests each week to find bugs, test out the performance and see how fun the game was.