Categories
Advanced Tutorials

The relationship between Player and User

Preface

One of the most common concepts found in video games is that of a user controlled avatar. The player takes control of an avatar, which can be anything depending on the theme of the game (a human, a vehicle, an exotic spaceship) to navigate and interact with the game world.

In this tutorial we will explore how this concept is applied in Crayta and how to set it up in your game.

When getting on board Crayta one of the first things you are asked to do is to setup your Avatar using a range of controls to customize the body, hair, clothing etc.

Crayta games revolve around players that control human avatars. Crayta provides a camera system, player collisions, interaction etc, making it very easy to create first or third person games.

In UE4 there is a very similar concept of having a Character Class which in Crayta would be the Player template and a Player Controller Class which is similar to the User template in Crayta.

User

A User is a type of entity which references the account of a person connected to the game. When you connect to a Crayta game a User entity will be spawned from the available User template and assigned to you.

All Crayta games are required to have a User template available. Only a single one is needed, since there isn’t any way to choose or set the active User template. Crayta will automatically detect and use it.

A default User template is provided in all new projects created which can be used to attach scripts and widgets.

Player

The User entity doesn’t have any physical representation in the game World, so you can’t see or interact with other connected Users.

When a new user is connected to the game and a User entity is spawned. Crayta will also attempt to spawn a Character entity from a Player template, unless a OnUserLogin method  has been added to a script. If that is the case then Crayta will not spawn a Player template and it is up to your game logic to handle this.

All player templates are required to have a root Character entity (other entity types may become available in the future.)

Spawning a character entity will cause the game to render the user’s avatar and add a default camera.

A default Player template is added in all new projects created.

In contrast with the User template, a Player template exposes a list of properties allowing you to customize a number of settings like the jump height or running speed of the Player.

User/Player relationship

Each user connected to the game has at any instant a User entity spawned and referenced to their account and also a Player (Character) which is their avatar which is active in the game World.

Both entities are based from the User and Player templates available in the game.

Firstly, let’s take a look on how data is shared and used between the User and the Player. If you create a new empty game and search for the Crayta packages called Health and Auto-Respawn and install both of them.

If you look at the Templates section of the Library you will see Player Health and User Spawn. Feel free to add the Player Health to your Player template and the Spawn template to your User under the Entities panel.

Example of installing the userSpawn template on the User entity.

Now, let’s look at the scripts and open both the spawnUserScript and the healthScript. Firstly, in the spawnUserScript let’s look at the function OnPlayerDied() in it you should see where we pass in the player and then in the schedule we destroy the player avatar before we respawn the character based on the User data.

function SpawnUserScript:OnPlayerDied(player, fromEntity)
	
	self.properties.onUserDied:Send(self:GetEntity(), fromEntity)
	
	if not self.respawnTask then
		self.respawnTask = self:Schedule(
			function()
				Wait(self.properties.respawnDelayTime)
				if self.properties.respawnActive then
					player:Destroy()
					self:SpawnInternal(self.isLobby)
				end
			end
		)
	end
end

Now, let’s inspect the healthScript and look at the SendDeathNotificationToAll function. We don’t need to go too in-depth here, but we can can see variable called deadUser this stores the data from the User and then in the variable below called deadUsername we use this access to get the Username of the player who died.

function HealthScript:SendDeathNotificationToAll(fromEntity, selfMsg, otherMsg, youKilledMsg)
	
	-- who died
	local deadUser = self:GetEntity():IsA(Character) and self:GetEntity():GetUser() or nil
	local deadName = deadUser and deadUser:GetUsername() or self:GetEntity():FindScriptProperty("name") or "?"

There is no need to run the preview; but feel free to play around with the health and respawn packages to help you understand this relationship.

Note: You may have to make sure you have checked the option to Spawn on Login to the spawnUserScript to make it useable.

A game can have a single User template but can have multiple Player templates.

The User entity provides you with a method to change the active Player template that is used by each player:

This is quite powerful since it allows you to easily change the behavior of your game depending on the game state. For example you can have one Player template for the lobby area with an Orbit camera and players not being able to jump, a first person Player template for the main game action time and another one for players spectating and not actively playing.

You can easily add more Player (Character) templates to your game using the Primitives window. All you need to do is select it and right-click and then select Create Template …

Here is an example of how to change the Player template from a script attached to a User entity:

local MyUserScript = {}

MyUserScript.Properties = {
    { name = "playerTemplate", type = "template"}
}

function MyUserScript:ChangePlayerTemplate()
  self:GetEntity():SpawnPlayer(self.properties.playerTemplate)
end

And here is an example of getting a reference to the active Player (Character) from a script attached to a User entity:

function MyUserScript:GetActivePlayer()
   return self:GetEntity():GetPlayer()
end

And a more advanced case: getting a reference to the Player entity of all connected users from any script running on the server:

GetWorld():ForEachUser(
   function(userEntity)
       local username = userEntity:GetUsername()
       local player = userEntity:GetPlayer()
       -- do something with the player 
       -- e.g. access script methods or player properties
   end
)