Now you can be a human male or female, and an orc male or female!
4 new arenas
figured I’d add some documentation here as well since I almost lost this all 3 nights ago. Here’s the full arenamanager and teleport script just incase I get sloppy again!
using UnityEngine;
using UnityEngine.UI;
using Mirror;
using System.Collections.Generic;
namespace GameZero
{
[DisallowMultipleComponent]
public class ArenaManager : NetworkBehaviour
{
[SerializeField] private bool allowSolo;
[SerializeField] private Instance arena1;
[SerializeField] private Instance arena2;
[SerializeField] private Instance arena3;
[SerializeField] private Instance arena4;
[SerializeField] private GameObject preview1Prefab;
[SerializeField] private GameObject preview2Prefab;
[SerializeField] private GameObject preview3Prefab;
[SerializeField] private GameObject preview4Prefab;
[SyncVar] public int instanceId = -1;
private RawImage selectedPreview;
private Instance selectedArena;
public static ArenaManager singleton;
private void Awake()
{
// Initialize singleton
if (singleton == null) singleton = this;
}
private void Start()
{
// Hide all preview images at start
preview1Prefab.SetActive(false);
preview2Prefab.SetActive(false);
preview3Prefab.SetActive(false);
preview4Prefab.SetActive(false);
}
[Command(requiresAuthority = false)]
public void CmdSetSelectedArena(int instanceId)
{
Instance arena = GetInstanceTemplate(instanceId);
if (arena != null)
{
GameObject previewPrefab = GetPreviewPrefabForArena(arena);
// Update the selected arena on the client only
selectedArena = arena;
selectedPreview = previewPrefab.GetComponent<RawImage>();
this.instanceId = instanceId;
// Show the preview image of the selected arena
previewPrefab.SetActive(true);
// Hide the preview images of other arenas
if (arena != arena1) preview1Prefab.SetActive(false);
if (arena != arena2) preview2Prefab.SetActive(false);
if (arena != arena3) preview3Prefab.SetActive(false);
if (arena != arena4) preview4Prefab.SetActive(false);
// Update the preview images on all clients
imagepreviewsRpc();
}
}
[ClientRpc]
public void imagepreviewsRpc()
{
if (instanceId == 1)
preview1Prefab.SetActive(selectedPreview == preview1Prefab.GetComponent<RawImage>());
else if (instanceId == 2)
preview2Prefab.SetActive(selectedPreview == preview2Prefab.GetComponent<RawImage>());
else if (instanceId == 3)
preview3Prefab.SetActive(selectedPreview == preview3Prefab.GetComponent<RawImage>());
else if (instanceId == 4)
preview4Prefab.SetActive(selectedPreview == preview4Prefab.GetComponent<RawImage>());
}
private GameObject GetPreviewPrefabForArena(Instance arena)
{
if (arena == arena1)
return preview1Prefab;
else if (arena == arena2)
return preview2Prefab;
else if (arena == arena3)
return preview3Prefab;
else if (arena == arena4)
return preview4Prefab;
else
return null;
}
public void OnArena1ButtonClicked()
{
Debug.Log("Arena 1 selected");
CmdSetSelectedArena(arena1.instanceId);
}
public void OnArena2ButtonClicked()
{
Debug.Log("Arena 2 selected");
CmdSetSelectedArena(arena2.instanceId);
}
public void OnArena3ButtonClicked()
{
Debug.Log("Arena 3 selected");
CmdSetSelectedArena(arena3.instanceId);
}
public void OnArena4ButtonClicked()
{
Debug.Log("Arena 4 selected");
CmdSetSelectedArena(arena4.instanceId);
}
public void OnRandomArenaButtonClicked()
{
Instance[] arenas = new Instance[] { arena1, arena2, arena3, arena4 };
int index = Random.Range(0, arenas.Length);
CmdSetSelectedArena(arenas[index].instanceId);
}
public void CreateInstance()
{
if (selectedArena != null)
{
arenaonoff.onoff.SetArena(selectedArena.arenaName);
arenaonoff.onoff.StartArena();
}
else
{
Debug.LogError("No selected arena!");
}
}
public Instance GetInstanceTemplate(int instanceId)
{
Debug.Log("GetInstanceTemplate called with instanceId = " + instanceId);
List<Instance> availableInstances = new List<Instance> { arena1, arena2, arena3, arena4 };
foreach (Instance availableInstance in availableInstances)
{
Debug.Log("Checking instance: " + availableInstance.instanceId);
if (availableInstance.instanceId == instanceId)
{
Debug.Log("Found instance: " + availableInstance);
return availableInstance;
}
}
Debug.Log("Instance not found for instanceId = " + instanceId);
return null;
}
}
}
using UnityEngine;
using Mirror;
using HeathenEngineering.SteamworksIntegration;
using System;
using System.Linq;
namespace GameZero
{
[DisallowMultipleComponent]
public class PlayerArenaNpcTeleport : NetworkBehaviour
{
[Header("Components")]
public Player player;
[SyncVar] public int partyIdToJoin = -1; // initialize to -1 to indicate that it is not set yet
private Vector3 targetPos;
public static PlayerArenaNpcTeleport singleton;
// Start is called before the first frame update
void Awake()
{
// initialize singleton
if (singleton == null) singleton = this;
}
[Command(requiresAuthority = false)]
public void CmdLobbyCreator()
{
Debug.Log("Player assigned: " + player.name);
if (player != null) { PartySystem.FormParty(player.name); }
partyIdToJoin = player.party.party.partyId;
Debug.Log($"partyId = {partyIdToJoin}");
}
[Command(requiresAuthority = false)]
public void CmdLobbyJoiner()
{
Player[] players = FindObjectsOfType<Player>();
// loop through each player and do something with them
foreach (Player player in players)
{
if (!player.party.InParty())
{
PartySystem.AddToParty(partyIdToJoin, player.name);
}
}
}
[Command(requiresAuthority = false)]
public void CmdPartySetup()
{
//ArenaManager am = GameObject.FindWithTag("GameManager").GetComponent<ArenaManager>();
Instance instanceTemplate = ArenaManager.singleton.GetInstanceTemplate(ArenaManager.singleton.instanceId);
Debug.Log("Cmdpartysetup called");
Debug.Log("instanceTemplate = " + instanceTemplate);
Player[] playersInParty = PartySystem.GetPlayersInParty(partyIdToJoin);
Debug.Log("Number of players in party: " + playersInParty.Length);
foreach (Player player in playersInParty)
{
Debug.Log("player name = " + player.name);
// loop through each player and do something with them
Debug.Log("player isServer: " + player.isServer + " hasAuthority: " + player.hasAuthority + " isClient: " + player.isClient + " isLocalPlayer: " + player.isLocalPlayer);
// collider might be in player's bone structure. look in parents.
if (player != null)
{
Debug.Log("player is not null");
// only call this for server and for local player. not for other
// players on the client. no need in locally creating their
// instances too.
if (player.isServer || player.isLocalPlayer)
{
Debug.Log("player is server or local player");
// required level?
if (player.level.current >= instanceTemplate.requiredLevel)
{
Debug.Log("player met level requirement");
// can only enter with a party
if (player.party.InParty())
{
Debug.Log("player is in a party");
// is there an instance for the player's party yet?
if (instanceTemplate.instances.TryGetValue(player.party.party.partyId, out Instance existingInstance))
{
// teleport player to instance entry
if (player.isServer)
{
Debug.Log("player has authority!!");
Vector3 entry1Pos = existingInstance.entry1?.position ?? Vector3.zero;
Vector3 entry2Pos = existingInstance.entry2?.position ?? Vector3.zero;
// Determine the target position for the player based on the player index
int playerIndex = Array.IndexOf(playersInParty, player);
Vector3 targetPos = (playerIndex % 2 == 0) ? entry2Pos : entry1Pos;
// Call the CmdWarpToEntry() method to move the player to the target position
if (player.isServer || (player.isClient && player.hasAuthority))
{
// Call the CmdWarpDrive() method only if the player is owned by a client
player.movement.Warp(targetPos);
}
Debug.Log("Teleporting " + player.name + " to existing instance=" + existingInstance.name + " with partyId=" + player.party.party.partyId);
}
}
// otherwise create a new one
else
{
Instance instance = Instance.CreateInstance(instanceTemplate, player.party.party.partyId);
NetworkServer.Spawn(instance.gameObject);
if (instance != null)
{
Debug.Log("instance is not null");
// teleport player to instance entry
Debug.Log("player isServer: " + player.isServer + " hasAuthority: " + player.hasAuthority + " isClient: " + player.isClient + " isLocalPlayer: " + player.isLocalPlayer + "isowned" + player.isOwned);
if (player.isServer)
{
Debug.Log("has authority");
// Get the entry positions for the instance
Vector3 entry1Pos = instance.entry1?.position ?? Vector3.zero;
Vector3 entry2Pos = instance.entry2?.position ?? Vector3.zero;
// Determine the target position for the player based on the player index
int playerIndex = Array.IndexOf(playersInParty, player);
Vector3 targetPos = (playerIndex % 2 == 0) ? entry2Pos : entry1Pos;
// Call the CmdWarpToEntry() method to move the player to the target position
if (player.isServer || (player.isClient && player.hasAuthority))
{
if (player.GetComponent<NetworkIdentity>().hasAuthority)
{
Debug.Log("client has authority for cmdwarpdrive");
// Check if client is still connected
if (!NetworkServer.connections.ContainsKey(player.GetComponent<NetworkIdentity>().connectionToClient.connectionId))
{
// Client has disconnected, do something
Debug.Log("client has disconnected, do something!");
}
else
{
// Call the CmdWarpDrive() method only if the player is owned by a client
player.movement.Warp(targetPos);
Debug.Log("player passed all checks, warping successfully");
}
}
Debug.Log("player didn't have authority, still warping just in case");
player.movement.Warp(targetPos);
}
Debug.Log("Teleporting " + player.name + " to new instance=" + instance.name + " with partyId=" + player.party.party.partyId);
}
else { Debug.Log("player is not server!!"); }
}
else if (player.isServer) player.chat.TargetMsgInfo("There are already too many " + instance.name + " instances. Please try again later.");
}
}
else
{
Debug.LogError("No existing instance found!");
}
}
}
}
}
}
}
}
4-25 update
so I’ve been working on the arena for some time now and thought i’d update you guys with some information about how it works, what it’s capable of and where it’s going in the future.
as you can see in this video there is an npc that opens a dialogue, when you open that dialogue you can create the lobby or browse a lobby. once a lobby is created other players can join in. after everyone selects ready and a map is chosen you can start the session and everyone is ported to their respective entry, odd players go to entry1 even players go to entry 2. after 10 seconds the gates open and they can begin fighting. warcraft has a 20 minute timer(I believe, it could be 25 minuters though?). I would like to have a timer like that eventually, right now everyone is in the same party which could produce problems down the line, but it works for now. The big feature here is synchronizing the maps over the network instead of locally. This means I can add any map and it should work regardless of size or shape. So seeded procedural arenas are a possibility. With that said, this is a huge breakthough for our team and I am proud to announce the arrival of networked arenas to GameZero.
There are 3 scripts that make this work. A player arena npc teleport script which handles all the group management and teleportation, an instance manager which handles synchronizing the instances over the network, and an arenamanager which handles the selection of instances and definition of which instance has been selected during the selection process. Over 400 lines of code were added to the ummorpg data structure to make this a reality. I hope you enjoy the video.
moving forward
here’s some things I’m working on that I think heathen can handle, I just dunno how to unlock it’s potential yet. 1.) I would like to have a search feature for the browser where users type in a string and it tries to find the closest result. 2.) when users select ready they are qued into a group which is used to identify their place in the session(which side they are on, etc.) 3.) the start button needs to take all users and port them to their respective place through navmesh.warp. I am using this instead of a new scene. it may seem counterintuitive but I have a working model now that seems to do the trick nicely for one person(not so well for more than one). right now I have the browser setup to find and create lobbies, you can select read or not ready. I have a button through ummorpg that handles porting at the moment, but I would like to convert that into the ‘start session button’ which would handle all the features in 3. figured I’d put this all here to document how I am going to use heathen.
here’s a video to show you guys where I stand and what needs to be done, you can see the start session button does nothing at this time and i have to talk to another npc to initiate the port to an instance. I had the button on the other guy but it was causing some problems so I have the two side by side for testing at this time.
Heathen+Ummorpg
- ok so I have some information to share if you would like it
- regarding heathen and ummorpg. I found almost everything I needed was already coded. It was just a matter of using unity’s event system and putting the right methods into the proper events. I was able to create/join/start sessions using everything that had already been coded. It was kind of hard to understand at first but very simple once I was shown how it operates. Also I was able to implement a very primitive leaderboard system, but there is still much more that can be done. I will probably continue work in this field for my master’s degree and feel that heathen will be the primary target of my thesis.
- With that said I am having problems with my dungeon arena disappearing and portals within that prefab disappearing at the start and have yet to find what is destroying them. Right now he ports to the dungeon and then it appears about 5 seconds after he ports. I would like it to be there beforehand with a portal out of the arena should anyone find themself locked inside(it resides about 150m in the air with 15m tall walls so there is no way out except a portal for now). Eventually I would like to add a timer to the arena that kicks players from the arena after 3-5 minutes much like warcraft.
Long time no update
We have abandoned fizzysteamworks/steammanager/steamlobby and gone all in with heathen. Built on a solid foundation this should be the proper approach for integrating steam into our project. Unfortunately all data was lost during this process, but we are rebuilding from the ground up. Here are all the new npcs together!
New reddit community
you can find out more information about game zero on reddit! We now have our own community. Join in and tell us about your experiences in viridia!
I’m writing this tutorial for a friend but think it would be great to have more documentation online regarding steam and ummorpg integration. I got a lot of information from scorphius’ video on youtube and his discord channel.
as you can see in the video you can easily hook up fizzysteamworks to ummorpg to integrate steam capabilities into your project. The steps I followed to get this job done were as follows. download these two scripts.
http://mas3d.website/steamworksaddons/UILogin.cs
http://mas3d.website/steamworksaddons/SteamLobby.cs
you want to put these in the assets folder of your project, and remove your ummorpg ui/uilogin.cs script. once that is done you can remove your server and play and create account buttons for ummorpg because this script will log you in automatically. You need a copy of your game on steam on both computers, and they need to be able to run the machines concurrently.
The tricky part is to get the steamid of the computer which will host this game and put it in the ip address for the build. That way you will login to this machine using steam. Once you build the server put it on the host machine and run the client.
there is a checkbox on the network manager for ummorpg which says “Auto start server build”. you need to have that checked for the server build and unchecked for the client build.
provided you follow the directions and use these helpful tips you should be able to log into your ummorpg project using steam credentials. you can see this in action with gamezero which is available for purchase on steam!
Steam Approved
finally got the game approved for steam. release date is scheduled for sometime in July, but it will probably be in beta phase for 6 months. check it out here:
Proto Factor Models
Added the lion from proto factor. Looks great with The defender set from hit animations!