How do you make a Character Selection..?

Hello!

How do I make the character selection work for me…?

This is one of the character’s script.

script.Parent.MouseButton1Click:Connect(function()

	game.Players.LocalPlayer.Character:WaitForChild("Humanoid").Health = 0
game:GetService("ReplicatedStorage"):WaitForChild("ClassicSword").Parent = game.Players.LocalPlayer.Backpack
end)

I added a wait there but it still doesn’t work. Like wait 3.1 and 3.2 and so on.
This script is under a text button.

3 Likes

Hello!

So I’m assuming this is a gui button that when pressed will put an item inside the user’s backpack? If so, this method you are using is not going to work because guis utilize local scripts which only work on the client.

→ See here to learn more: Understanding the Difference Between Server Side and Client Side

This means that re-parenting the object into the player’s backpack will only be seen by the player and the activation event tied to the object will only fire inside of local scripts stored inside the object (which in your case isn’t what you want because you’re using the ‘ClassicSword’ which uses Scripts opposed to Local Scripts that can only execute code on the server).

So to fix this issue we will firstly utilize RemoteEvents. A remote event is an object that the client can reference and call upon in order to relay information the server

–>> See more here: Remote Events and Callbacks | Documentation - Roblox Creator Hub

In this case, we would want to use RemoteEvents to act as a middle-man in telling the server to clone the sword in ReplicatedStorage and put it into the Player’s backpack to ensure that the sword is given to the player on the Server rather than just the Client. We do this by creating a new RemoteEvent inside of ReplicatedStorage (I would rename it to something like “CharacterSelect” for organization purposes) and then reference it inside the local script of the button.

-- example
local REP = game:GetService('ReplicatedStorage')
local CharacterSelect = REP:WaitForChild('CharacterSelect')
local Button = script.Parent -- referencing the button

Button.MouseButton1Click:Connect(function()
-- some code ...

CharacterSelect:FireServer('Insert Character Name Here') 
end)

Once you reference it within a variable, you would then create a regular script inside of ServerScriptService in order for the server to detect when the RemoteEvent is fired.

Here’s an example of what that may look like:

local REP = game:GetService('ReplicatedStorage')
local CharacterSelect = REP:WaitForChild('CharacterSelect')
local Sword = REP:WaitForChild('ClassicSword')

-- in remote events, the first argument of the function
-- is always the player, the second is whatever the client
-- passes through it. So in this case, we passed the string
-- 'Insert Character Name Here'
CharacterSelect.OnServerEvent:Connect(function(Player, Character)
    if Character == 'Insert Character Name Here' then

           -- the ":Clone()" method duplicates an
           -- instance its called upon. This is favorable
           -- in this case because we don't want to just re-parent
           -- the sword inside the player's backpack because that
           -- we need the original sword to clone for constant use.
           local Backpack = Player.Backpack
           local SwordClone = Sword:Clone()
           SwordClone.Parent = Backpack

           -- p.s. if you still want to kill the player, 
           -- I'd advise you still do it on the server.

    end
end)

Then you’re pretty much done. If you have any questions, feel free to ask!

3 Likes

Thanks!, But Do I put the sword in The Replicated Storage?

Yes I apologize for leaving out the reference to the sword.

Okay so thank you, but I want the character types to be like The strongest battle grounds, Like you switch to a character then you automaticlly reset and then switch to the character. If I do the same thing it give’s me the sword but it disappears after resetting.

[A LOT OF ADVANCED STUFF IS MENTIONED HERE, SO PLEASE ASK QUESTIONS TO GAIN FURTHER UNDERSTANDING]

Ahh I had a feeling. Alright so if that’s the case, we want to utilize values. In this case, we can add an attribute when the Player joins that designates them a ‘CharacterSelected’ value. When the player is given this value, we then want to; 1.) detect the value change 2.) find the selected character’s data within a module storing all relevant data 3.) applying the changes of switching character to the player via utilizing a metatable method for scalability.

We can start off by firstly utilizing the event trigger ‘Player.PlayerAdded’ in a script inside of ServerScriptService to do this.

-- inside this new hypothetical script inside of ServerScriptService
local PLRS = game:GetService('Players')
local REPS = game:GetService('ReplicatedStorage')

-- [THIS MODULE IS MENTIONED LATER ON IN THE POST; IGNORE IT FOR NOW]
local RealCharacters = require(REPS:WaitForChild('RealCharacters'))

-- config variables
local DEFAULT_CHARACTER = 'Insert Default Character Name Here'
-- if you want to define a character that is automatically
-- set if the player hasn't chosen on before; then utilize
-- this variable above.

PLRS.PlayerAdded:Connect(function(Player)
     
     -- set attribute
     Player:SetAttribute('CharacterSelected', DEFAULT_CHARACTER)

     -- detect whenever the attribute changes
     Player:GetAttributeChangedSignal('CharacterSelected'):Connect(function()
          local Value = Player:GetAttribute('CharacterSelected')
          --[AGAIN THIS STUFF BELOW IS MENTIONED FURTHER INTO THE
          --RESPONSE SO KEEP READING]
          local SELECTED = RealCharacters[Value]
          SELECTED:LoadCharacterAsSelf(Player, true)
     end) 

     -- get character instance to apply selected character data to.
     Player.CharacterAdded:Connect(function(Character)
             local Value = Player:GetAttribute('CharacterSelected')
             local SELECTED = RealCharacters[Value]
             SELECTED:LoadCharacterAsSelf(Player)
     end)

end)

FOR MORE ABOUT ATTRIBUTES:
→ See more here: Attributes - Now Available!

What this basically does is add a piece of data to the Player object inside the game that the server and client can read later down the line.

Now revisiting the old server script, (we don’t need to change anything in the button script), we will modify the RemoteEvent detection to change the value of the ‘CharacterSelected’ value within the player if the given character name is valid.

We can check if the character name is valid by utilizing a module as a ‘lookup’ table.

-- inside old script that detected RemoteEvent.
local REP = game:GetService('ReplicatedStorage')
local CharacterSelect = REP:WaitForChild('CharacterSelect')
local RealCharacters = require(REP:WaitForChild('RealCharacters'))

CharacterSelect.OnServerEvent:Connect(function(Player, CharacterName)
       -- here we check if the character name is inside the 'Characters'
       -- module by indexing with brackets

       local SELECTION = RealCharacters[CharacterName]
       if SELECTION then
           -- tell the server to label the player as this 'Selected Character'
           Player:SetAttribute('CharacterSelected', CharacterName)
       end
end

Once we set the Player’s CharacterSelected to the character name, the event we connected earlier; “GetAttributeChangedSignal”, will fire, causing the code inside that function block to run. In that function’s case, it will be firing a function that will be held within the character data within the module we required. So let’s create the module.

I recommend creating this module script in ReplicatedStorage as it can be accessed by both client and server which is great for this type of game.

This module script will serve as the main hub for selected character data as mentioned before: I named it ‘RealCharacters’ for the sake of this example but you can name it whatever you see fits.

Now for this example I’m going to be using OOP or Object Orientated Programming as it’s perfect for creating new characters on the fly.

→ See more here: All about Object Oriented Programming

To start creating a new character we first want to define a class by utilizing metatables.

→ See more here: Metatables | Documentation - Roblox Creator Hub

-- this is our class table
local CHARACTER_CLASS = {}
CHARACTER_CLASS.__index = CHARACTER_CLASS -- circular index for class to work

-- we will define some properties our character class will have
CHARACTER_CLASS.Backpack = {} -- items the character loads the player with

-- other properties as an example to give 
-- you the jist of what this is capable of.
CHARACTER_CLASS.Health = 250
CHARACTER_CLASS.Speed  = 100

-- we define a function that only this class can have.
function CHARACTER_CLASS:LoadCharacterAsSelf(Player : Player, ReloadCharacter : boolean)
-- brief introduction to the variable "self" if you're not knowledgable of it.
-- self is used inside functions that have ':' connecting it.
-- It's implication is that we are referring to the table the function
-- is being called in which in this case is the uniquely created class
-- we call this in. PLEASE TAKE SOME TIME TO LEARN THIS STUFF! IS
-- MEGA IMPORTANT AND VERY VERY USEFUL TO KNOW!

-- reload character
if ReloadCharacter then
       Player:LoadCharacter()
end

-- clone all existing items found within the class's backpack
local Backpack = self.Backpack
for i = 1, #Backpack do
       local Index = Backpack[i]
       Index:Clone()
       Index.Parent = Player
end

-- this was the optional stuff i put in the class to show as an example
local Health, Speed = self.Health, self.Speed

local Humanoid = Player.Character:FindFirstChildWhichIsA(''Humanoid")
Humanoid.MaxHealth  = Health
Humanoid.Health         = Health
Humanoid.WalkSpeed  = Speed



end

-- a function used in creating 'instances' (unique clones) of the class we just created.
local function CreateCharacter(data)
       -- asserts are used to error if the condition inside of them isn't true.
       -- in this case, we're checking if data sent to this function is a valid table.
       -- if not? error and find the problem by debugging.
       assert(type(data) == 'table', DATA NEEDS TO BE GIVEN) 

       -- any table created inside a metatable class will have it's referenced drawn back
       -- to it which isn't good because we want a unique 'inventory' for each character
       -- created. 

       data.Backpack = data.Backpack or {}

       return setmetatable(data, CHARACTER_CLASS)
end

-- this is our module space
local RealCharacters = {}

return RealCharacters

Now let’s actually implement our new character class.

-- this is going to be inside the 'RealCharacters' module space
-- we are going to use that 'CreateCharacter' func we created before
local REP = game:GetService('ReplicatedStorage')

local RealCharacters = {
    -- we can use "{}" instead of "()" at the end because a table
    -- is the functions only argument
    Example1 = CreateCharacter{
         Backpack = {
             REP:WaitForChild('ClassicSword')
         },
         Speed = 16,
         Health = 250
    } 
}

return RealCharacters

And with that you should be done. Just remember to set the attribute to a character created inside the table; otherwise it will error.

I may have made a lot of mistakes writing this, so please exercise caution and make your own adjustments to my methodology. There is of course a lot more that can be done to make this more optimal, but I wanted to make it pretty simple. A project like this requires a lot of knowledge so don’t skip out on the learning aspect and make sure you really understand what’s being presented! I am not a good teacher by any means, but this is the best I can come up with at the moment.

1 Like

Thank you!, But where do I put everything?, Like module scripts and more?

The module script goes into ReplicatedStorage; and the other two scripts mentioned go right into ServerScriptService

I’m getting these errors and I’m not getting the characters (2 characters which Sword and HyperlaserGun)

ReplicatedStorage.RealCharacters:58: Expected ')' (to close '(' at line 52), got '='  -  Studio - RealCharacters:58
Requested module experienced an error while loading  -  Server - 2:4
Stack Begin  -  Studio
Script 'ServerScriptService.2', Line 4  -  Studio - 2:4
Stack End  -  Studio
Requested module experienced an error while loading  -  Server - 1:6
Stack Begin  -  Studio
Script 'ServerScriptService.1', Line 6  -  Studio - 1:6
Stack End  -  Studio

Paste the whole ‘RealCharacters’ module script here

1 Like

Where?‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎

1 Like

On this forum post -----------------------------------

1 Like
-- this is our class table
local CHARACTER_CLASS = {}
CHARACTER_CLASS.__index = CHARACTER_CLASS -- circular index for class to work

-- we will define some properties our character class will have
CHARACTER_CLASS.Backpack = {} -- items the character loads the player with

-- other properties as an example to give 
-- you the jist of what this is capable of.
CHARACTER_CLASS.Health = 250
CHARACTER_CLASS.Speed  = 100

-- we define a function that only this class can have.
function CHARACTER_CLASS:LoadCharacterAsSelf(Player : Player, ReloadCharacter : boolean)
	-- brief introduction to the variable "self" if you're not knowledgable of it.
	-- self is used inside functions that have ':' connecting it.
	-- It's implication is that we are referring to the table the function
	-- is being called in which in this case is the uniquely created class
	-- we call this in. PLEASE TAKE SOME TIME TO LEARN THIS STUFF! IS
	-- MEGA IMPORTANT AND VERY VERY USEFUL TO KNOW!

	-- reload character
	if ReloadCharacter then
		Player:LoadCharacter()
	end

	-- clone all existing items found within the class's backpack
	local Backpack = self.Backpack
	for i = 1, #Backpack do
		local Index = Backpack[i]
		Index:Clone()
		Index.Parent = Player
	end

	-- this was the optional stuff i put in the class to show as an example
	local Health, Speed = self.Health, self.Speed

	local Humanoid = Player.Character:FindFirstChildWhichIsA('Humanoid')
		Humanoid.MaxHealth  = Health
		Humanoid.Health         = Health
		Humanoid.WalkSpeed  = Speed



end

-- a function used in creating 'instances' (unique clones) of the class we just created.
local function CreateCharacter(data)
	-- asserts are used to error if the condition inside of them isn't true.
	-- in this case, we're checking if data sent to this function is a valid table.
	-- if not? error and find the problem by debugging.
	assert(type(data) == 'table',

	-- any table created inside a metatable class will have it's referenced drawn back
	-- to it which isn't good because we want a unique 'inventory' for each character
	-- created. 

	data.Backpack = data.Backpack or {}

	return setmetatable(data, CHARACTER_CLASS)
end

-- this is our module space
local RealCharacters = {}
local REP = game:GetService('ReplicatedStorage')

local RealCharacters = {
	-- we can use "{}" instead of "()" at the end because a table
	-- is the functions only argument
	Example1 = CreateCharacter{
		Backpack = {
			REP:WaitForChild('ClassicSword')
		},
		Speed = 16,
		Health = 250
	} 
}

return RealCharacters


-- this is going to be inside the 'RealCharacters' module space
-- we are going to use that 'CreateCharacter' func we created before

1 Like

Also quick mention before I debug

-- inside this new hypothetical script inside of ServerScriptService
local PLRS = game:GetService('Players')
local REPS = game:GetService('ReplicatedStorage')

-- [THIS MODULE IS MENTIONED LATER ON IN THE POST; IGNORE IT FOR NOW]
local RealCharacters = require(REPS:WaitForChild('RealCharacters'))

-- config variables
local DEFAULT_CHARACTER = 'Insert Default Character Name Here'
-- if you want to define a character that is automatically
-- set if the player hasn't chosen on before; then utilize
-- this variable above.

PLRS.PlayerAdded:Connect(function(Player)
     
     -- set attribute
     Player:SetAttribute('CharacterSelected', DEFAULT_CHARACTER)

     -- detect whenever the attribute changes
     Player:GetAttributeChangedSignal('CharacterSelected'):Connect(function()
          local Value = Player:GetAttribute('CharacterSelected')
          --[AGAIN THIS STUFF BELOW IS MENTIONED FURTHER INTO THE
          --RESPONSE SO KEEP READING]
          local SELECTED = RealCharacters[Value]
          SELECTED:LoadCharacterAsSelf(Player, true)
     end) 

     -- get character instance to apply selected character data to.
     Player.CharacterAdded:Connect(function(Character)
             local Value = Player:GetAttribute('CharacterSelected')
             local SELECTED = RealCharacters[Value]
             SELECTED:LoadCharacterAsSelf(Player)
     end)

end)

this is a fixed version of that player script I made, just replace the old one with what I just wrote down

Requested module was required recursively  -  Server - RealCharacters:6
Stack Begin  -  Studio
Script 'ReplicatedStorage.RealCharacters', Line 6  -  Studio - RealCharacters:6
 Stack End  -  Studio
Requested module experienced an error while loading  -  Server - 2:4
 Stack Begin  -  Studio
 Script 'ServerScriptService.2', Line 4  -  Studio - 2:4
Stack End  -  Studio
Requested module experienced an error while loading  -  Server - 1:6
Stack Begin  -  Studio
Script 'ServerScriptService.1', Line 6  -  Studio - 1:6
Stack End  -  Studio
1 Like

Make this change to the module script:
image
I forgot to put a “)” at the end of the assert

 20:33:27.158  ServerScriptService.1:30: attempt to index nil with 'LoadCharacterAsSelf'  -  Server - 1:30
  20:33:27.158  Stack Begin  -  Studio
  20:33:27.158  Script 'ServerScriptService.1', Line 30  -  Studio - 1:30
  20:33:27.158  Stack End  -  Studio

replace the code in the script with this:

-- inside this new hypothetical script inside of ServerScriptService
local PLRS = game:GetService('Players')
local REPS = game:GetService('ReplicatedStorage')

-- [THIS MODULE IS MENTIONED LATER ON IN THE POST; IGNORE IT FOR NOW]
local RealCharacters = require(REPS:WaitForChild('RealCharacters'))

-- config variables
local DEFAULT_CHARACTER = 'Insert Default Character Name Here'
-- if you want to define a character that is automatically
-- set if the player hasn't chosen on before; then utilize
-- this variable above.

PLRS.PlayerAdded:Connect(function(Player)
     
     -- set attribute
     Player:SetAttribute('CharacterSelected', DEFAULT_CHARACTER)

     -- detect whenever the attribute changes
     Player:GetAttributeChangedSignal('CharacterSelected'):Connect(function()
          local Value = Player:GetAttribute('CharacterSelected')
          --[AGAIN THIS STUFF BELOW IS MENTIONED FURTHER INTO THE
          --RESPONSE SO KEEP READING]
          local SELECTED = RealCharacters[Value]
          SELECTED:LoadCharacterAsSelf(Player, true)
     end) 

     -- get character instance to apply selected character data to.
     Player.CharacterAdded:Connect(function(Character)
             local Value = Player:GetAttribute('CharacterSelected')
             local SELECTED = RealCharacters[Value]
             SELECTED:LoadCharacterAsSelf(Player)
     end)

end)
1 Like

ClassCharacterSelectionSystem.rbxl (52.7 KB)

here’s a download to a place copy i just made of the system fully working if you need something to compare it with.

1 Like

Still the same error. --------------------

1 Like