We almost have a complete game! Our game still lacks something, though: If you try to export your project and run it from the .exe, you may realize that the debug window that displays the print() commands and the error messages are not displayed. It's used only during development.
We will have to show the remaining health points and current score on the game screen; this is called a HUD ("Heads-up display")
HUD creation Edit
We will create our HUD in a scene and then instantiate it in the game scene. So let's create a new scene called "HUD", add an object with a camera at (0,0,0) and put it in orthographic mode. Thus, it will not render the perspectives, our HUD will be "flat".
We want to show two things:
- An health bar will indicate the player's remaining health points
- Text to display the player's score
Create a health barEdit
Create a new model, add a block 128*16*1 sized (you can give it a color if you want) and then create a new animation for this model. Name it "deplete" and put his length to 100 frame.
On the first frame, put a key-frame on pivot shift and stretching without changing anything.
On the last frame, put a key-frame by changing the offset pivot on X by "-64" (half the length of the bar) and put the stretching on X to 0.
If all goes well, you should get an animation of your bar that gradually reduce until disappearing. If you do not succeed, go to the project "Doomsday Carrot Rampage" and see how the model "healthbar" is built or export it and add it to your project.
Create a text objectEdit
[Outdated|You can use the Font feature now]Edit
CraftStudio does include tools to easily display text yet, so we will use a little trick to display any text easily.
Start by creating a new map called "HUDmap" and add a new tileset named "ASCIItable" sized 16.
We will fill our tileset with the ASCII table. The ASCII standard assigns to each character a number from 0 to 255 (http://www.ascii.cl/) good thing, our tileset can hold 256 different blocks. Each block (numbered 0 to 255) will correspond to a character. For example, the character "1" got the ASCII code 49.
So the block ID 49 should represent a "1". This could be done by hand but it would be a bit tedious. This is possible to find ready tilseset with the ASCII table. You can use the table below, or recover on here, for example: http://dwarffortresswiki.org/index.php/Tileset_repository.
Just copy your texture in Craftsutio to create your tileset. Finally, add an horizontal block line starting at (0,0,0) to be able to identify easily the first row of the map.
"HUDmap" map and put it at top left. Set your scene as the starting one in order to launch it and view the results. Adjust the position and size of objects until you like it.
Return in your "HUD" scene, add a new object named "healthbar" with your health bar model, put it at the bottom right for example. Create another object named "score", associate it with the
Now we need to integrate our scene "HUD" in the game As for the enemies and bullets, we will instantiate our new prefabricated scene, but this time only once and at the launch of our game.
In your main scene, attach a new script to your "Camera" object and call "init_HUD." In Awake() we will instantiate our HUD:
CraftStudio.Instantiate( "HUD", CraftStudio.FindAsset( "HUD" ) )
If you run the game, you should realize that there is a problem. Indeed, the HUD scene contains a camera, so when we instantiate the scene "game" (which also includes one), the game has two different cameras that will display different things on the screen game (or rather, the same things but seen under different angles). To remedy this we will simply move the scene HUD so that both cameras will not "film" the same thing. The "init_HUD"script becomes: function Behavior:Awake()
local HUD = CraftStudio.Instantiate( "HUD", CraftStudio.FindAsset( "HUD" ) ) HUD.transform:SetPosition( Vector3:New( 10000, 10000, 10000 ) ) end
Update HUD elements Edit
It only remains for us to ensure that our HUD elements are updated according to the variables "self.life" and "self.score" from our character. We will once again define methods specific to HUD objects and call those methods from the player's script using sendMessage().
Update health barEdit
Return to your scene "HUD" and associate a new script to your health bar ("gest_heathbar").
To control the state of our health bar he will have to manage its animation. All functions that handles animation model applies to a model renderer component . In awake(), we will start by retrieve this component:
self.model = self.gameObject:GetComponent("ModelRenderer")
Then we will define which animation to use. We have only created one "deplete", it is therefore recovered with CraftStudio.FindAsset ("deplete") and we define it as current animation with:
By default, our animation will run in loop, we rather want it to be stuck on a still image. We just have to add:
Our animation will be blocked on the first frame: the one where the bar is full. To view our bar more or less empty, we simply change the point where the animation is stopped. So we will create a method that will update our bar:
function Behavior:Show(value) end
The function to change the frame that should display the animation is ModelRenderer:SetAnimationTime ([number] time), where the parameter is the wanted animation moment in second from the beginning. Here our animation consists of 100 frames with 30 frames per second. So, to show the 50th frame of an animation we will use: SetAnimationTime(50/30).
The life points varies between 0 and 10, so we will simply multiply it by 10 to get a value between 0 and 100. If the the player have 3 points (30%), we should display on the 70th frame: 100 - 3 * 10. We therefore obtain:
self.model: SetAnimationTime ((100 - value * 10) / 30)
We'll just have to complete the player's Damage() method to call our new "Show()" method :
self.bar: CraftStudio.FindGameObject = ("healthbar") self.bar: SendMessage ("Show", self.life)
Similarly, assign a new script "gest_score" to your "score" object.
This script will handle the "map" component of our object which can be retrieved with the "MapRenderer" component function : GetMap() :
self.map = self.gameObject:GetComponent("MapRenderer").GetMap()
As usual we create our update function:
function Behavior:Show(value) end
We'll have to show the contents of the variable "value" by changing the blocks displayed in the map. You can change the blocks with the map function Map:SetBlockAt( [number] x, [number] y, [number] z, [number] block id, [BlockOrientation] orientation ).
We will handle each characters from the variable value, one after the other with a "for" loop:
for i=1,string.len(value) do end
"i" will be the index of the character being processed. The ID of the block to be placed in the map corresponds to the ASCII character for that character. We will use the LUA function string.byte (value, i) which returns the ASCII code of "i-th" character in the string "value". The orientation of the blocks will be the default one (Map.BlockOrientation.North).
We therefore obtain: for i=1,string.len(value) do local char = string.byte(value,i) self.map:SetBlockAt( i, 0, 0, char, Map.BlockOrientation.North ) end
We will now add some text before our score. Add in the Awake ()
self.texte = "Score:" self:Show(0)
And change the Show() method to obtain:
function Behavior:Show(value) self.output == self.texte..value for i=1,string.len(self.output) do local char = string.byte(self.output i) self.map:SetBlockAt(i, 0, 0, char, Map.BlockOrientation.North) end end
The player's score will always be preceded by the text "Score:"
It remains for us to complete the player's AddPoint() method to call the"score" object Show() method :
self.TextScore = CraftStudio.FindGameObject( "score" ) self.TextScore:SendMessage( "Show",self.score)
(The text It may be not displayed correctly if your marker blocks in your "HUDmap" map are misplaced ...)
And "voila"! You already have a good starting point to create your own games. We did not used all the Crafstudio's API functions, so do not hesitate to consult the reference to complete your scripting knowledge.
Final script :Edit
function Behavior:Awake() self.vitesse = 0.05 self.life = 10 self.score = 0 self.groundPlane = Plane:New( Vector3:Up(), -1 ) self.viseur = CraftStudio.FindGameObject( "viseur" ) self.cameraComponent = CraftStudio.FindGameObject( "Camera" ):GetComponent( "Camera" ) self.bulletPrefab = CraftStudio.FindAsset( "prefab_bullet" ) end function Behavior:Update() --gestion des déplacements local horizontal = CraftStudio.Input.GetAxisValue( "Horizontal" ) local vertical = CraftStudio.Input.GetAxisValue( "Vertical" ) local mouvement = Vector3:New( horizontal, 0, -vertical ) mouvement = mouvement * self.vitesse self.gameObject.transform:Move (mouvement) --gestion de l'orientation local mousePos = CraftStudio.Input.GetMousePosition() local mouseRay = self.cameraComponent:CreateRay( mousePos ) local distance = mouseRay:IntersectsPlane( self.groundPlane ) if distance ~= nil then local targetPos = mouseRay.position + mouseRay.direction * distance self.viseur.transform:SetPosition( targetPos ) self.gameObject.transform:LookAt( targetPos ) end --gestion des tirs if CraftStudio.Input.IsButtonDown( "Fire" ) then CraftStudio.Instantiate("bullet",self.bulletPrefab) end end --Fonction appelée lors d'une colision avec un ennemi function Behavior:Damage(damage) self.life = self.life -damage print ("il vous reste "..self.life.."points de vie") if self.life <= 0 then CraftStudio.LoadScene (CraftStudio.FindAsset("menu")) end --gestion affichage vie self.bar = CraftStudio.FindGameObject( "healthbar" ) self.bar:SendMessage( "Show",self.life) end --Fonction appelée lorsque le joueur tu un ennemi function Behavior:AddPoint(point) self.score = self.score + point print ("Vous avez "..self.score.." points") self.TextScore = CraftStudio.FindGameObject( "score" ) self.TextScore:SendMessage( "Show",self.score) end
function Behavior:Awake() self.model = self.gameObject:GetComponent( "ModelRenderer" ) self.model:SetAnimation( CraftStudio.FindAsset( "deplete" ) ) self.model:StopAnimationPlayback() end function Behavior:Update() end function Behavior:Show(value) self.model:SetAnimationTime( (100- value*10) / 30 ) end
function Behavior:Awake() self.map = self.gameObject:GetComponent("MapRenderer"):GetMap() self.texte = "Score :" self:Show (0) end function Behavior:Update() end function Behavior:Show(value) self.output = self.texte..value for i=1,string.len(self.output) do local char = string.byte(self.output,i) self.map:SetBlockAt( i, 0, 0, char, Map.BlockOrientation.North ) end end