UI Getting Started

From Camelot Unchained Wiki
Jump to: navigation, search


This article is about Camelot Unchained UI development. It is not intended as a tutorial for html5 or JavaScript, and assumes basic knowledge of both.

Note that this article is subject to change significantly throughout the course of the development of Camelot Unchained.

Background[edit | edit source]

Each user interface element runs in an isolated instance of the browser and gets it's own client.exe process. Communication with the game client is handled through the cuAPI interface provided by the game client.

The Basics[edit | edit source]

Each user interface is defined by it's .ui configuration file, for example chat.ui is the file which describes the chat UI.

These files are found in the game installation folder bin/<instance>/client/assets/webui where <instance> is a number indicating a particular instance of the client. During the internal testing phase, these instances are numbered 4 (internal test client) and 10 (pre-alpha client). I will be using instance 4 (internal test client) in the examples in this document.

The chat.ui configuration file looks like this

Hello World - your first UI[edit | edit source]

Now we know how to kick start a UI, lets begin with the obligatory Hello World example. Remember that we are working in the bin\4\client\assets\webui folder for this. I recommend using notepad++ [1] for simple html/javascript editing, but you can use any editor or visual studio or the new Visual Studio Code

Note however, when creating the .ui file it must be created in either ANSI or UTF8 without BOM format. Visual Studio creates UTF8 files with BOM and if visual studio is used to create the .ui file it will cause an exception in the game client [ UIWindow.cpp line 290 !doc.HasParseError() ] when it tries to load the UI. If you get this error, open the .ui file in notepad++ and set the encoding to ANSI or UTF8 without BOM and re-save.

Create a helloworld.ui file and save it in the webui folder.

 {
   "_LICENSE": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.",
   "name": "helloworld",
   "mainFile": "helloworld/helloworld.html",
   "rect": {
     "left": 621,
     "top": 280,
     "right": 820,
     "bottom": 320
   }
 }

Create a helloworld subfolder.

In the helloworld folder, create a text file named helloworld.html with the following HTML:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>helloworld</title>
  </head>
  <body>
    <div id="msg">Hello World</div>
  </body>
</html>

Now to load this UI in the client, start the client then in the chat box use the /openui command to load your helloworld custom UI

 /openui helloworld

and if everything is as it should be, you will see the words hello world, quite small, and feint in black above your character.

Lets add a stylesheet and change the look a bit.

Edit helloworld.html and add the following line after the <title>helloworld</title> line.

 <link rel="stylesheet" type="text/css" href="helloworld.css">

Create a helloworld.css file in the helloworld folder with the following content:

 body {
   margin: 0;
   background-color: rgba(0,0,0,0.1);
 }
 #msg {
   text-align: center;
   color: white;
   font-size: 16pt;
 }

To get the client to pick up these changes, close and re-open the UI by typing the following into chat:

 /closeui helloworld 
 /openui helloworld

That's about as far as we can take the hello world UI without turning it into a not a hello world UI. So lets move on to something more useful.

Basic Combat Text UI[edit | edit source]

and it really is very basic! So like before lets create the basic UI components:

bct.ui (goes in the webui folder)

 {
   "_LICENSE": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.",
   "name": "bct",
   "mainFile": "bct/bct.html",
   "rect": {
     "left": 621,
     "top": 280,
     "right": 820,
     "bottom": 320
   }
 }

bct.html (goes in the bct subfolder that you will need to create)

<!DOCTYPE html>  
<html xmlns="http://www.w3.org/1999/xhtml">  
<head>  
    <title>bct</title> 
    <link rel="stylesheet" type="text/css" href="bct.css"> 
</head>  
<body>  
<div id="msg"></div>   
</body>  
</html>

bct.css (goes in the bct subfolder)

body { 
    margin: 0; 
} 
#msg { 
    text-align: center; 
    color: white; 
    font-size: 16pt; 
    font-weight: bold; 
}

In order to be able to report damage to ourselves, or damage we do to others we must be able to track our own, and our targets health. To do this we need to use the following APIs

 cuAPI.OnEnemyTargetHealthChanged
 cuAPI.OnCharacterHealthChanged

In order to be able to do this, we must tell the client we want access to these APIs. This is done through the bct.ui file by adding "handlesEnemyTarget": true, and "handlesCharacter": true options as follows:

 {  
   "_LICENSE": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.",  
   "name": "bct",  
   "mainFile": "bct/bct.html",  
   "rect": {  
       "left": 621,  
       "top": 280,  
       "right": 820,  
       "bottom": 320  
   }, 
   "handlesEnemyTarget": true,
   "handlesCharacter": true
 }

Now we need to write some code to monitor the health of both self and your target, and to display combat text when the health changes. In order to do this we will add some javascript.

Note, if developing the UI as a project, using TypeScript rather than JavaScript is recommended, but not a requirement. For this small tutorial though we will just use JavaScript.

Add the following line to bct.html just above </body>

 <!DOCTYPE html>  
 <html xmlns="http://www.w3.org/1999/xhtml">  
 <head>  
   <title>bct</title> 
   <link rel="stylesheet" type="text/css" href="bct.css"> 
 </head>  
 <body>  

<div id="msg">

 <script src="bct.js"></script> 
 </body>  
 </html>

And create a basic bct.js file in the bct folder (which can be used as a template for other UI code)

 (function(){ 
   var init = function() { 
     // initialise 
     // ... ui code goes here ... 
   }; 
   if (typeof cuAPI !== "undefined") {  
     if (cuAPI.initialized) {  // cuAPI already initialised 
       init(); 
     } else { 
       cuAPI.OnInitialized(init); 
     } 
   }  
 })();

Now lets add the code to track our own and our targets health and to display the combat text when we detect damage.

Add the following code to bct.css

 body { 
   margin: 0; 
 } 
 #msg { 
   text-align: center; 
   color: white; 
   font-size: 16pt; 
   font-weight: bold; 
 }
 .fadeOut { 
   opacity: 0; 
 } 
 .fadeIn { 
   opacity: 1; 
 }

Replace the code in bct.js with the code below (this is the full code to implement basic combat text)

 (function(){
   var ourHealth, targetHealth, targetName, timer;  
   var msg = document.getElementById("msg"); 
   var fadeOut = function() { 
     msg.className = "fadeOut"; 
   }; 
   var clearTextIn = function(ms) {  
     if (timer) clearTimeout(timer);  
     timer = setTimeout(function() { fadeOut(); timer = null; }, ms);  
   }; 
   var showCombatText = function(old, current, self) {  
     if (old !== undefined) { 
       if (old > current) {  
         msg.innerText = current-old;  
         msg.style.color = self ? 'white' : 'red'; 
         msg.className = "fadeIn"; 
       } else if (self) { 
         msg.innerText = "+" + (current-old);  
         msg.style.color = 'rgb(0,255,0)'; 
         msg.className = "fadeIn"; 
       } 
       clearTextIn(1000); 
     } 
   };
   var init = function() {  
     // initialise  
     // track our own health  
     cuAPI.OnCharacterHealthChanged(function(health, maxHealth) { 
       showCombatText(ourHealth, health, true);  
       ourHealth = health;  
     }); 
     // track targets health 
     cuAPI.OnEnemyTargetHealthChanged(function(health, maxHealth) {  
       if (health === -1 && maxHealth === -1) { 
         // no target 
       } else { 
         showCombatText(targetHealth, health, false);  
         targetHealth = health;  
       } 
     }); 
     // track target change 
     cuAPI.OnEnemyTargetNameChanged(function(name) { 
       if (name.length === 0) { 
         targetName = targetHealth = undefined; 
       } else { 
         if (targetName !== name) { 
           targetHealth = undefined; 
         } 
         targetName = name; 
       } 
     });
   }; 
   if (typeof cuAPI !== "undefined") {  
     if (cuAPI.initialized) {  // already initialised 
       init(); 
     } else { 
       cuAPI.OnInitialized(init); 
     } 
   }  
 })();

Don't forget to /openui bct to start the combat text UI!

A brief explanation of the code[edit | edit source]

In init() we register for OnCharacterHealthChanged and OnTargetHealthChanged events. In these events we call showCombatText() in order to display the change in health as either damage or health gain (if this is for our own character) and then we record the new health value.

Also in init() we register for the OnTargetnameChanged event and use that to clear any current target health, so that we know to not display any damage info when we receive the next target health event (which will contain the initial health of the target rather than a change in health).

In showCombatText() we calculate the health change, and display that value in an appropriate colour, then start a timer that will fade the text out after a defined period (1 seconds, or 1000 milliseconds) in this case.

Conclusion[edit | edit source]

The Basic Combat Text UI is meant to be used as an example of how to create a UI from scratch using the basics of the UI interface, rather than a fully functional UI, which is beyond the scope of this article.

There are a number of improvements that can be made to the UI; the main one is to have 3 separate slightly offset areas for self damage, self healing and target damage figures with their own fadeout timers. With a bit more effort these could be made to animate (float) upwards or downwards allowing the display of multiple damage indicators at once of each type.

Also the length of time the text remains visible should be relative to the damage amount, because at the moment a dot that inflicts a small amount of damage every 1 seconds, currently effectively permanently displays a -dmg value because the new damage is done just as the old value is being cleared.

Also, the whole design of this UI is flawed, because it is using change in health values to work out damage, and as such it cannot distinguish between self inflicted damage and external inflicted damage or perhaps even damage from multiple attackers. But hey, this is still only alpha!

For an improved version, which supports healing, self damage and target damage visible at the same time, see Mehuge BCT

Debugging your UIs[edit | edit source]

The client supports chrome developer tools for debugging UIs. In order to connect to the debugger for a UI, first point your brower at http://localhost:8088/ then look for the name of your UI in the list, and click the links.

The first thing to look for are error messages in the console view (at the bottom) as these will list any syntax errors or undefined functions that are preventing your UI from doing anything. The next step is to use the Source tab and set breakpoints so you can step through and inspect your code as it runs.

You can also use console.log('a message'); to log information to the clients console log (debug.log in the bin/4/client folder) which if you can tail it using wintail or cygwin's tail command, you can watch what your UI is doing (based on the messages you log) while it is running.