Torque3D
  • General
    • Welcome!
    • Features
    • Release Notes
      • Version 4.0.3
      • Version 4.0.2
      • Version 4.0.1
      • Version 4.0
      • Version 3.10.1
      • Version 3.10
      • Version 3.9
      • Version 3.8
      • Version 3.7
      • Version 3.6.2
      • Version 3.6.1
      • Version 3.6
  • Getting Started
    • Introduction
      • What's the Torque3D Engine?
    • Getting Familiar
      • Getting a Copy
        • Torque Project Manager
        • Downloading it Yourself
      • Getting Ready for Launch
        • Running Pre-Built Binaries
        • Building the Engine Yourself
      • Launching the Game
        • Opening the Example Level
        • Launching the Editors
      • Your First Game
        • Introduction To The Engine
        • The Module System
        • Creating an empty gamemode
        • Adding a Player
        • Adding a Coin
        • Adding a win-condition
        • Custom coin asset
        • Counting coins
        • Adding some effects
        • Supporting multiplayer
        • Adding a Scoreboard GUI
        • Keeping the scoreboard up-to-date
      • Deep Dive: BaseGame Directory Structure
    • Best Practices
    • Porting a Legacy Project
  • For Artists
    • Assets
      • What are Assets?
      • How to Create a New Asset
      • Working With Assets
      • Deep Dive: Creating a New Asset Type
    • Art
      • File Formats
      • 3D Art
        • Shape Specifications
        • Coordinates System
        • Mounting Shapes
        • Animation
        • Player Setup
        • Blender -> Torque3D Pipeline
      • 2D Art
        • Working with Adobe Substance
    • Animation
    • GUI
      • Loading and Initializing a GUI
      • Expanding a GUI via Script
      • How to Network GUIs
    • Materials
      • Material Mapping
      • Material Animation
    • Terrain
    • Shaders
    • Lighting
    • Audio
  • For Designers
    • Base Classes
      • SimObject
      • SimGroup
      • SceneObject
      • Scene
      • Datablocks
    • Game Classes
      • Creating an Object
      • Destroying an Object
      • Gameplay Scripting
        • Spawning an Object from Gameplay Code
    • Modules
      • What are Modules?
      • How to Create a New Module?
      • Making a Module do Things
      • Installing Existing Modules
        • Where to Get More Modules
    • Scenes and Levels
      • How to Create a New Level
      • How to Load a Level
        • Deep Dive: Level Loading Scripts
      • How to Edit Levels
        • Opening a Level in the Editor
        • Spawning Objects from the Asset Browser
        • Working with Scenes
        • Using SimGroups
        • Changing a Level's PostEffects
        • Deep Dive: LevelAsset Companion Files
    • Game Modes
      • Creating a New GameMode
      • Making a Level Use Your GameMode
      • Adding Gameplay Code to Your GameMode
    • AI
      • Navmesh
      • Objects
      • Scripting
    • Inputs
      • Inputs and Keybinds
        • ActionMap
        • Bind Functions
        • ActionMap Stack
    • Localization
    • Editors
      • Changing Editor Settings
      • World Editor
        • Scene Editor
        • ConvexShape Editor
        • Terrain Editor
        • Terrain Painter
        • Material Editor
        • Spline-Based Tools
          • Mesh Road Editor
          • River Editor
          • Decal Road Editor
        • Datablock Editor
        • Particle Editor
        • Decal Editor
        • Forest Editor
        • Navmesh Editor
        • Deep Dive: Creating Your Own Editor
        • Shape Editor
      • GUI Editor
        • Interface Details
  • For Programmers
    • Compiling the Engine
      • Setup Development Environment
        • SDK and Library Installation
        • Git
        • Cmake
        • Creating a Fork on Github
      • Create a Project
        • Creating a Project With CMake
        • Creating a Project With the Project Manager
      • Compiling
        • Compiling in Windows
        • Compiling in Linux
        • Compiling in MacOS
      • Building the Project Manager
    • Introduction
    • Code Style Guidelines
    • Expanding the Engine
      • Creating a New Object Class
      • Exposing Object Classes to Script
        • addProtectedField
      • Adding a New Library to the Engine
    • Major Components of the Engine
      • Core
        • Console
        • Platform
      • Audio
        • SFX
      • Rendering
        • GFX
        • Render Bins
      • Physics
        • Stock T3D Physics
        • Physics Wrapper
          • PhysX
          • Bullet
        • Classes
    • Rendering
    • Math
    • Networking
      • Client and Server Commands
    • Physics
    • Collision
    • Scripting
      • TorqueScript
        • What is TorqueScript?
        • Basic Syntax
        • Variables
        • Types
        • Operators
        • Control Structures
        • Functions
        • Objects
        • Module Interop
          • QueueExec
          • RegisterDatablock
          • CallOnModules
          • ModuleExec
        • API Reference
      • Other Languages
        • C-Interface
    • File Inputs/Outputs(I/O)
    • API Reference
Powered by GitBook
On this page
  • Writing the client-side logic
  • Adding the hooks on the server
Edit on GitHub
Export as PDF
  1. Getting Started
  2. Getting Familiar
  3. Your First Game

Keeping the scoreboard up-to-date

In order to update scoreboard we need to do two things:

  1. Write the logic for updating the UI

  2. Adding hooks to trigger this logic where necessary

Writing the client-side logic

We want to be able to add/update a player in the scoreboard when they join and remove a player when they leave.

In data/CoinCollection/client/gui/scoreBoard.tscript add the following section to the bottom:

//-----------------------------------------------------------------------------
// ScoreBoardGUI data handler methods
//-----------------------------------------------------------------------------

function ScoreBoardGUI::upsertPlayer(%this, %clientID, %name, %score, %kills, %deaths) {
    %text = StripMLControlChars(%name);
    if (%score !$= "null")
        %text = setField(%text, 1, %score);
    %text = setField(%text, 2, %kills);
    %text = setField(%text, 3, %deaths);

    // Update or add the player to the control
    if (ScoreBoardGUIList.getRowNumById(%clientId) == -1)
        ScoreBoardGUIList.addRow(%clientId, %text);
    else ScoreBoardGUIList.setRowById(%clientId, %text);

    // Sorts by score
    ScoreBoardGUIList.sortNumerical(1, false);
    ScoreBoardGUIList.clearSelection();
}

function ScoreBoardGUI::removePlayer(%this, %clientId) {
    PlayerListGuiList.removeRowById(%clientId);
}

So lets see what this does. The upsertPlayer function first creates a new variable %text which stores name, score, kills and deaths with columns seperated by tabs. Then we check if there is already stored a row with that id. If not, then we add a new row else we update the row with that id. Finally we sort the list by column 1 (the second column because it starts at 0).

The removePlayer function simply removes the row with the player's ID.

In order to trigger these two functions we will use the network message system. Let's add the client-side listeners right away in the top of the scoreBoard.tscript file:

//-----------------------------------------------------------------------------
// Callbacks
//-----------------------------------------------------------------------------
addMessageCallback('MsgClientWelcome', SBGUIWelcome);
addMessageCallback('MsgClientJoin', SBGUIPlayerJoined);
addMessageCallback('MsgClientDrop', SBGUIPlayerLeft);
addMessageCallback('MsgClientScoreChanged', SBGUIScoreChanged);

function SBGUIWelcome(%msgType, %msgString, %clientName, %clientId,
        %isAI, %isAdmin, %isSuperAdmin) {
    ScoreBoardGUI.clear();
    ScoreBoardGUI.upsertPlayer(%clientId, detag(%clientName), 0, 0, 0);
}

function SBGUIPlayerJoined(%msgType, %msgString, %clientName,
        %clientId, %score, %kills,
        %deaths, %isAI, %isAdmin, %isSuperAdmin) {
    ScoreBoardGUI.upsertPlayer(%clientId, detag(%clientName), %score, %kills, %deaths);
}

function SBGUIPlayerLeft(%msgType, %msgString, %clientName, %clientId) {
    ScoreBoardGUI.removePlayer(%clientId);
}

function SBGUIScoreChanged(%msgType, %msgString, %clientName,
        %clientId, %score, %kills, %deaths) {
    ScoreBoardGUI.upsertPlayer(%clientId, detag(%clientName), %score, %kills, %deaths);
}

Here we use detag on the clientName, that is because the clientName is a tagged string, so we need to look up the clientName ID to get the string.

Adding the hooks on the server

We have four different callbacks we need to implement. Let's start with the callback MsgClientScoreChanged, we will trigger that one whenever we pick up a coin. In data/CoinCollection/server/coin.tscript add:

messageAll('MsgClientScoreChanged', -1, %col.client.playername,
    %col.client, %col.client.coinsFound,
    %col.client.kills, %col.client.deaths);

Right after:

%col.client.coinsFound++; // Automatically starts at 0

The three other hooks should happen whenever a client enters or leaves the game. So let's open up data/CoinCollection/gamemode.tscript and change onClientEnterGame and add onClientLeaveGame :

function CoinCollectionGameMode::onClientEnterGame(%this, %client) {
    //Set the player name based on the client's connection data
    %client.setPlayerName(%client.connectData);

    %this.spawnControlObject(%client);

    // Welcome the client
    messageClient(
            %client, 'MsgClientWelcome',
                    "\c2Welcome to the Torque demo app %1.",
                    %client.playername,
                    %client,
                    %client.isAiControlled()
            );

    // Inform the client about everyone else
    foreach(%other in ClientGroup) {
        if (%other == %client) {
            continue;
        }

        messageClient(%client, 'MsgClientJoin', -1,
                    %other.playername,
                    %other, %other.coinsFound,
                    %other.kills, %other.deaths);
    }

    // Inform everyone else about the client
    messageAllExcept(
            %client,
                    "-1",
                    'MsgClientJoin',
                    "\c1 % 1 joined the game.",
                    %client.playername,
                    %client,
                    %client.coinsFound,
                    %client.kills, %client.deaths,
                    %client.isAiControlled()
            );
}

function CoinCollectionGameMode::onClientLeaveGame(%this, %client) {
    // Inform everyone that the client left
    messageAllExcept(
            %client,
                    "-1",
                    'MsgClientDrop',
                    "\c1 % 1 left the game.",
                    %client.playername,
                    %client,
                    %client.isAiControlled()
            );
}
PreviousAdding a Scoreboard GUINextDeep Dive: BaseGame Directory Structure

Last updated 2 years ago