Stuff that I learned from the Godot Step-by-Step starter tutorials. I’m hoping I can shorten someone else’s journey (plus this is how I document stuff).
A Godot Game is a tree of Scenes (*.tscn files from the project explorer) and Scenes are made up of Nodes (the primitive types in a Godot game).
In the 2D starter tutorial we made four scenes:
Main.tscn
Player.tscn
Mob.tscn
HUD.tscn
You can see from the list of scenes (below) that the logical grouping of a Main controller scene – a Player and a Mob (Bad Guys) scene and the HUD scene is split out. The Main scene is the starting point for a Godot game. Scenes can be instantiated (many times as objects within another Scene – and edited independently – or all objects can be changed globally by editing the *.tscn) .
As you can see above – each Scene had a related Godot Script file (*.gd) that holds the code for that Scene (more on that later).
Scenes are your basic building block and design element. For each component in the game – make a Scene.
Nodes have Properties and can receive callbacks to update every frame. They can be extended with new properties (and functions). They can be nested under another node as a child (the tree structure).
Properties can be edited in the Inspector Window to the Right of the Godot Editor. The Inspector displays a node’s properties in “Title Case” which are available to GDScript code (As lowercase – with words separated by an underscore – hover over Properties in the editor to check them).
Example: a script on a 2D Sprite Character that starts with:
extends Sprite2D
Which grants access to all the inherited properties of a 2D Sprite visible in the Inspector – which means you can write:
var texture = load("res://icon.svg")
$Sprite2D.texture = texture
Doing Stuff
Game Window Size Settings
Click on Project -> Project Settings to open the project settings window and in the left column, open the Display Window tab. There, set “Viewport Width” to 480 and “Viewport Height” to 720.
Player Input
There are two main ways to process input :
The built-in input callbacks, mainly _unhandled_input(). Like _process(), is a built-in virtual function that is called every time there is a keypress. Use if if you need to react to events that don’t happen every frame (like hitting Space to jump). To learn more about input callbacks, see Using InputEvent.
The Input singleton. A singleton is a globally accessible object. Godot provides access to several in scripts. It’s the right tool to check for input every frame.
We use the Input singleton here as we need to know if the player wants to turn or move every frame.
Managing input is usually complex, no matter the OS or platform. To ease this a little, a special built-in type is provided, InputEvent. This datatype can be configured to contain several types of input events. Input events travel through the engine and can be received in multiple locations, depending on the purpose.
Here is a quick example, closing your game if the escape key is hit:
func _unhandled_input(event):
if event is InputEventKey:
if event.pressed and event.keycode == KEY_ESCAPE:
get_tree().quit()
However, it is cleaner and more flexible to use the provided InputMap feature, which allows you to define input actions and assign them different keys. This way, you can define multiple keys for the same action (e.g. the keyboard escape key and the start button on a gamepad). You can then more easily change this mapping in the project settings without updating your code, and even build a key mapping feature on top of it to allow your game to change the key mapping at runtime!
You can set up your InputMap under Project > Project Settings > Input Map and then use those actions like this:
func _process(delta):
if Input.is_action_pressed("ui_right"):
# Move right.
Animations
Adding animations to 2D Animation Node is a little non-obvious.
To create one, find the Sprite Frames property under the Animation tab in the Inspector and click “[empty]” -> “New SpriteFrames”. Click again to open the “SpriteFrames” panel: (Note that there are two tabs sitting next to each other there in the panel – both called Sprite Frames – which is kind of like an expanded info box for the first Sprite Frames tab – doesn’t look obvious but it is when you know it’s there).
On the left is a list of animations. Click the “default” one and rename it to “up”.
The Animation Sprite Pane is called “Sprite Frames” down the bottom (in blue) – (it’s easy to lose it if you click on something else and then go looking for it in the obvious Animation footer): Add sprites by clicking on the little “folder” icon (next to the play/pause strip).
Anti-Aliasing
I was a bit disappointed with how scenes looked when I started playing with my own demo scenes but once I found the Anti-Aliasing settings I got more comfortable. Under Project -> Project Settings.
The difference between this:
and this:
GDScript
GDScript is an indent-based language – which is really annoying. I make tabs and spaces by muscle memory all the time when coding and it bites me in the ass. Cutting and Pasting code is also fraught with danger. But annoyances aside I do really like it as a scripting language – it’s simple, well designed (in that interfaces are consistent and logical) and does what you want with a minimum of fuss.
The only other gripe is that there are a lot of setting strings for references in the editor so you need to be careful when using them in code that you spell them correctly and are aware that they are case sensitive. For example you may have set an animation called “funnyWalk1” in the editor so when you code it in GDScript you need to use something like below:
if velocity.x != 0:
$AnimatedSprite2D.animation = "funnyWalk1"
It would be nice if there was some kind of style checker or linter that could roll over your code for strings and do a fuzzy match against the instances in the editor and tell you when you used FunnyWalk1 or funnywalk1 or funnywolk1 or funmyWalk1 etc.
Inspector Access
Using the export keyword on the first variable speedallows us to set its value in the Inspector
@export var speed = 400 # How fast the player will move (pixels/sec).
Here, rotation is a property inherited from the class Node2D, which Sprite2D extends. It controls the rotation of our node and works with radians.
The func keyword defines a new function. After it, we have to write the function’s name and arguments it takes in parentheses. A colon ends the definition, and the indented blocks that follow are the function’s content or instructions.
Note: Notice how _process(), like _init(), starts with a leading underscore. These are Godot’s built-in functions you can override to communicate with the engine.
_process()is called every frame (it’s the equivalent of Update() in Unity).
Godot 4.0: nonexistent function “instance” in base PackedScene Instance is Godot 3, on 4 is instantiate()
PathFollow, offset (the path length moved from the beginning in distance units). PathFollow2D, you’ll see that it no longer has offset as an attribute – the new name is “progress”.
V3 has linear_interpolation in V4 it has been replaced with lerp.
This is an embarrassing post. Some days are there to simply remind you that you don’t really know what you are doing. I spent two days trying to track down why my networking scripts were not working when I ran them in the editor. Turns out that the editor makes it’s own internal network stack when running a project which does not connect to the normal ethernet ports on your machine.
Top Tip ! Build you project and run it natively if you are using TCP/IP connections ! Don’t try and run it in the editor (not even for quick checks or small simple projects) it simply won’t work.
The really nice thing is after I got it all working I built a touch screen controller for Android phones that can be used as input in a Unity game.
I’ve been working with the FFTWindow analysing audio input and needed a quick way to view the coordinate space of the grid. I’m surprised there is not a feature in Unity to do this (at least not that I could find). This is a quick script to help with debugging coordinates in the Scene View.
Grid Line Coordinates on the XY plane
The script basically just draws a set of Axis in the scene and labels the x,y,z coordinates. It’s best when you are viewing along the alignment of an axis (I use the XYZ Gizmo in the corner to switch between them).
In 3D view with Cyan and Magenta sample lines.
You can toggle the Z plane on and off to make it easier to see just the XY plane and there is a few sample lines to show how it might be useful.
I was using to analyse audio spectrum data on the fly like this:
The blue Logarithmic representation of the PCM signal in red
Code
Here is the code for the grid lines/coordinates below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class GridLines : MonoBehaviour
{
public int X_Axis_Length = 10;
public int Y_Axis_Length = 10;
public bool Z_Plane = true;
public int font_Size;
public bool show_Test = true;
private GUIStyle guiStyle = new GUIStyle(); //create a new variable
public void OnDrawGizmos()
{
// Axis
Gizmos.color = Color.red; // X
Gizmos.DrawLine(new Vector3((X_Axis_Length / -1), 0, 0), new Vector3((X_Axis_Length), 0, 0));
Gizmos.color = Color.green; // Y
Gizmos.DrawLine(new Vector3(0, (Y_Axis_Length / -1), 0), new Vector3(0, (Y_Axis_Length), 0));
Gizmos.color = Color.blue; // Y
Gizmos.DrawLine(new Vector3(0, 0, (X_Axis_Length / -1)), new Vector3(0, 0, (X_Axis_Length))); // assumes Z Axis is the same length as the X
//Gizmos.DrawWireSphere(centre, Radius);
guiStyle.fontSize = font_Size;
int x = -X_Axis_Length;
int y = -Y_Axis_Length;
while (x < X_Axis_Length)
{
Handles.Label(new Vector3(x, y, 0), (x + "," + y));
while ( y < Y_Axis_Length)
{
Handles.Label(new Vector3(x, y, 0), (x + "," + y));
y++;
}
x++;
y = -Y_Axis_Length;
}
if (Z_Plane)
{
int z = -X_Axis_Length; // Z Axis
y = -Y_Axis_Length;
while (z < X_Axis_Length) // Z Axis
{
Handles.Label(new Vector3(0, y, z), (z + "," + y));
while ( y < Y_Axis_Length)
{
Handles.Label(new Vector3(0, y, z), (z + "," + y));
y++;
}
z++;
y = -Y_Axis_Length;
}
}
if (show_Test)
{
// Test lines
Gizmos.color = Color.cyan;
Gizmos.DrawLine(Vector3.zero, new Vector3(4, 5, 0));
Gizmos.DrawWireSphere(Vector3.zero, 0.2f);
Gizmos.DrawCube(new Vector3(4, 5, 0), new Vector3(0.2f, 0.2f, 0.2f));
Gizmos.color = Color.magenta;
Gizmos.DrawLine(new Vector3(-3, 3, -3), new Vector3(2, 2, 2));
Gizmos.DrawWireSphere(new Vector3(-3, 3, -3), 0.2f);
Gizmos.DrawCube(new Vector3(2, 2, 2), new Vector3(0.2f, 0.2f, 0.2f));
}
}
}
Quick Tip: I have been working on a TCP/IP Networking project using a client/server architecture. The client (and the server for that matter) are both relatively small code bases and the UI and object count are really low in the scene. I had been struggling with CPU load in the project and feverishly trying to work out why my code was baking the CPU (and GPU!). I’d assumed it was something stupid I had done in a loop with the networking structures I was not that familiar with. It’s really not easy to concentrate on new code when your laptop fan is literally screaming at you! I’d hit Play and the CPU would spike almost immediately. So I would switch to my local terminal and scrape through the open ports and network connections looking for a smoking gun. Turns out it was the default frame rate in the Editor trying to deliver the fastest graphics performance it could on my PC – and with such a low object count and and very simple graphics being asked for it was running like a Formula One race car when all I wanted was an old jalopy.
This is my CPU on Speed
Solution: Set Target Frame Rate!
A Unity project will attempt to run your project as fast as possible. Frames will be rendered as quickly as they can (limited by your display device’s refresh rate).
There are two ways to control frame rate:
Application.targetFrameRate – controls the frame rate by specifying the number of frames your game tries to render per second. (I wrote a script to use this – see below).
QualitySettings.vSyncCount – specifies the number of screen refreshes to allow between frames. (look for it in the Editor Settings). For a 60Hz display, setting vSyncCount=2 will cause Unity to render at 30fps in sync with the display.
Note that mobile platforms ignore QualitySettings.vSyncCount and use Application.targetFrameRate to control the frame rate.
The default value of Application.targetFrameRate is -1. (the platform’s default target frame rate)
I set mine using the script to 20 and when I hit Play got this result:
This is my CPU chilling out
using UnityEngine;
public class SetFrameRate : MonoBehaviour
{
[SerializeField] // Just so you can check it in the inspector
private int FrameRate = 20; // 20 is really low but got my CPU down to < 10% - 30 is the target for mobile and was < 20% CPU usage
//private int FrameRate = -1; // reset to default
private void Awake()
{
Application.targetFrameRate = FrameRate;
}
}
I attached it to my Camera object.
Set Frame Rate
One interesting behavior of setting this using a script in Unity 2020.3.26f1 was that once it was attached to the Camera object and Play was initiated for the first time it must have set the frame rate somewhere internally in the Engine. When I removed the script (for testing) the frame rate did not automatically reset to -1. I had to re-attach the script and update it to set the frame rate back to the default. I had a search of the settings in the Inspector and Preferences and couldn’t find a visible reference to it anywhere so you have to be careful if you are going to put this on a Production build that you reset it before releasing otherwise you might end up with a lower frame rate than what the platform could achieve by default.
Enough procrastination – back to sockets, ports and buffers.
How to make use of a message bus in Unity. This is a good solution to decouple components and logically organise how your game runs. Instead of doing all the dragging in the editor of game objects and components into scripts the message bus works a bit like a proxy for the communication between scripts. This is good (makes complex things easier to manage – ie. no noodling of dependencies everywhere – and no heavy FindObject calls – plus I get to make a funny bus pun.
Events
When using Events your Objects become Subscribers and Publishers of actions. When you subscribe (or listen) to a particular event channel you get notified when something changes. These notifications go out to every object that is listening on the bus and each object script can respond to the message in their own way.
You can make this as complex as you like but it gets harder the more complex the data is that you are passing around on the bus. This example uses just four components and our events are nice and simple:
An enum that lists the Events on the Bus.
The YellowBus class that handles subscribtions and publishing (by using a dictionary of events)
A BigYellowBusController script that is attached to our bus game object and subscribes to all the “bus” events like starting, stopping at stops, taking on passengers, etc.
A PassengerController that is attached to the Bus Rider game objects (the coloured circles) which subscribes and reacts to rider type events like calling the bus, getting on and off, and ringing the bell when you want to get off.
EventsOnTheBus
This is just a simple collection of named constants that are meaningful for our events.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AdvancedCoding.YellowBus
{
public enum EventsOnTheBus
{
CALL_BUS, ALL_ABOARD, START_ENGINE, NEXT_STOP_PLEASE, STOP_ENGINE, EMERGENCY_STOP, END_O_THE_LINE
}
}
It exposes the Subscribe, Unsubscribe, and Publish methods.
Notice the using UnityEngine.Events directive at the top.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace AdvancedCoding.YellowBus
{
public class YellowBus
{
private static readonly IDictionary<EventsOnTheBus, UnityEvent>
Events = new Dictionary<EventsOnTheBus, UnityEvent>();
public static void Subscribe(EventsOnTheBus eventType, UnityAction listener)
{
UnityEvent thisEvent;
if (Events.TryGetValue(eventType, out thisEvent))
{
thisEvent.AddListener(listener);
}
else
{
thisEvent = new UnityEvent();
thisEvent.AddListener(listener);
Events.Add(eventType, thisEvent);
}
}
public static void Unsubscribe(EventsOnTheBus type, UnityAction listener)
{
UnityEvent thisEvent;
if (Events.TryGetValue(type, out thisEvent))
{
thisEvent.RemoveListener(listener);
}
}
public static void Publish(EventsOnTheBus type)
{
UnityEvent thisEvent;
if (Events.TryGetValue(type, out thisEvent))
{
thisEvent.Invoke();
}
}
}
}
BigYellowBusController
The controller on the bus is a subscriber to the events that it needs to react to and defines private methods of handling those events when they are broadcast. You can have any number of subscribers listening in to your events. In this example we only got two: The YellowBus and the Rider.
The Rider Controller does pretty much the same as the Big Yellow Bus Controller but only listens to those events that relate to the riders (some of which it shares with the Big Yellow Bus Controller). Now here is where it gets interesting… look at the NEXT_STOP_PLEASE parts. Start with the OnGui() method below on line 75 where a button press will publish the NEXT_STOP_PLEASE event. In the OnEnable() method our Subscription code calls the PressTheBell() private method (line 17) that let’s our game object handle the call and produce the text message. BUT the YellowBus Controller also subscribes to this event and handles it with the NeedToGetOff() method so that it knows it has to stop and let the passenger off (line 21, 59 and 95 in the BigYellowBusController script above). AAANNDDDD responds by triggering the STOP_ENGINE event and the END_O_THE_LINE event. See how they can chain together into larger behaviours and complex interactions between different game objects without actually entangling them at the script reference level.
Also be aware that on these scripts there is a subscription in OnEnable() and an Unsubscribe in OnDisable() (so we are not holding on to the listener when the game object disappears) the thing to note here is that we are listening to our own events. For example the RiderController triggers the CALL_BUS event and both our own script and the BigYellowBusController script react to it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AdvancedCoding.YellowBus
{
public class RiderController : MonoBehaviour
{
private bool _lightMeGUIup;
private string _message;
private GameObject _bus;
void OnEnable()
{
YellowBus.Subscribe(EventsOnTheBus.CALL_BUS, HopOnTheBus);
YellowBus.Subscribe(EventsOnTheBus.NEXT_STOP_PLEASE, PressTheBell);
YellowBus.Subscribe(EventsOnTheBus.ALL_ABOARD, JumpOn);
YellowBus.Subscribe(EventsOnTheBus.END_O_THE_LINE, JumpOff);
}
void OnDisable()
{
YellowBus.Unsubscribe(EventsOnTheBus.CALL_BUS, HopOnTheBus);
YellowBus.Unsubscribe(EventsOnTheBus.NEXT_STOP_PLEASE, PressTheBell);
YellowBus.Unsubscribe(EventsOnTheBus.ALL_ABOARD, JumpOn);
YellowBus.Unsubscribe(EventsOnTheBus.END_O_THE_LINE, JumpOff);
}
private void Start()
{
_bus = GameObject.Find("BigYellowBus");
}
private void PressTheBell()
{
_message = "I need to get off the bus !!";
_lightMeGUIup = true;
}
private void HopOnTheBus()
{
_message = "I need to get on the bus please!!";
_lightMeGUIup = true;
}
private void JumpOn()
{
_message = "Hi Driver!";
transform.parent = _bus.transform;
transform.position = new Vector3(0f, 0.2f, 0f);
}
private void JumpOff()
{
_message = "Thanks a lot!";
transform.parent = null;
transform.position = new Vector3(0f, -3f, 0f);
}
private void OnGUI()
{
if (GUI.Button(new Rect(160, 10, 100, 30), "Call Bus"))
{
YellowBus.Publish(EventsOnTheBus.CALL_BUS);
}
if (GUI.Button(new Rect(160, 50, 100, 30), "Bell Press"))
{
YellowBus.Publish(EventsOnTheBus.NEXT_STOP_PLEASE);
}
if (_lightMeGUIup)
{
GUI.color = Color.blue;
GUI.Label(new Rect(300, 40, 200, 20), "..." + _message);
StartCoroutine(DropGUI());
}
}
private IEnumerator DropGUI()
{
yield return new WaitForSeconds(2f);
_lightMeGUIup = false;
}
}
}
The other thing to note is that this message bus is like a party line – all messages are broadcast to anyone listening. All the passengers respond to the NEXT_STOP_PLEASE event as they all share the same code…but of course they could implement different controller scripts per player and all handle this event differently. It’s pretty powerful stuff.
Playtime in Bus Controller Land !
The code above was modified from an example in Game Development Patterns with Unity 2021: Explore practical game development using software design patterns and best practices in Unity and C#, 2nd Edition by David Baron – I recommend buying a copy it is the best book of this type that I have read – take yourself from a beginner to an intermediate Unity programmer. 🙂
I’ve been doing a lot of back end investigation the past few months – trying to build something to support future projects. Most of it’s just random poking about with new technologies and different ways of doing “stuff” but then sometimes while doing this you find something that is really cool and resonates with the way your brain works. For me this month it was Rust and RHEL WSL.
I got here by a round about route of looking at golang programming and docker integration for back end processing of a game. A way to offload non-game-critical systems to other processors. Like a high score system that keeps player profiles and scores online which can be called from within the game when needed. I know there are heaps of good services out there that do this – but I like poking into stuff and having that level of control.
The other thing is – sometimes stuff just makes sense. I’ve been a long time linux user and champion but have always been locked into a Wintel desktop in the studio due to the support needs of my audio kit. (I have a Line 6 KB37 which integrates the midi keyboard and guitar effects pedals into one unit – it’s freaking awesome and I’m terrified that one day it will break and be out of support). I’d run linux as a virtual machine and used cygwin and my favourite mobaXterm as a solution to this but while poking around within the docker community I came across WSL. The Windows Subsystem for Linux.
WSL
The Windows Subsystem for Linux is a compatibility layer which runs Linux binary executables natively on Windows. WSL v2 has a real Linux kernel (though there are some storage issues if you need more info read the docs). The default linux kernel is Ubuntu which I’m OK with – one of my favourite linux distributions is the Ubuntu Studio but I’ve been a Red Hat admin for a long time so am more comfortable there but there was no RHEL or CentOS supported platform. But then I found the RHWSL project by a Japanese Developer called Yosuke Sano and I was intrigued and immediately hooked.
Basically this is how I set up WSL on my Windows 10 Workstation.
C:\Users\zulu>wsl –list –online The following is a list of valid distributions that can be installed. Install using ‘wsl –install -d ‘.
C:\Users\zulu>WSL –list –all Windows Subsystem for Linux Distributions: Ubuntu (Default)
NAME FRIENDLY NAME Ubuntu Ubuntu Debian Debian GNU/Linux kali-linux Kali Linux Rolling openSUSE-42 openSUSE Leap 42 SLES-12 SUSE Linux Enterprise Server v12 Ubuntu-16.04 Ubuntu 16.04 LTS Ubuntu-18.04 Ubuntu 18.04 LTS Ubuntu-20.04 Ubuntu 20.04 LTS
C:\Users\zulu>wsl –set-default-version 2 For information on key differences with WSL 2 please visit https://aka.ms/wsl2 The operation completed successfully.
C:\Users\zulu>WSL –HELP Invalid command line option: –HELP Copyright (c) Microsoft Corporation. All rights reserved. Usage: wsl.exe [Argument] [Options…] [CommandLine]
I downloaded the RHWSL package from git, extracted it and run the executable to register the package with WSL and install the root file system and that was it.
This is how I set the RHWSL distribution as my default:
d:\RedHat\RHWSL>wsl -d RHWSL
Here are a bunch of useful links if you want to explore more:
If you want more info on WSL yosukes-dev has an “Awesome” resource list.
Rust
I started looking at Rust as part of a wider investigation into “modern” programming languages. I spent a few weeks looking at go (golang) and as much as it was a great multi-purpose language and super easy to start using I was a little gobsmacked at how large the binaries were (they are statically compiled) and being someone who is always on tiny machines I kept getting a niggling feeling that a whole system of go programs would be a big chunk of disk on a small device doing work that that might be easier done slower with a simple shell script (I exaggerate!). Anyway in a lot of the stuff I read golang and rust were comparable. I will come back to go as the community and contributions seemed most excellent.
Plus Rust made sense to me in places where Go didn’t. I really don’t have a logical excuse here or a well reasoned argument. Sometimes you just like a language cause it “feels” right. I had the same thing with Ruby. Anyway this is how I got started with Rust on the RHWSL…
https://www.rust-lang.org/learn/get-started If you’re a Windows Subsystem for Linux user run the following in your terminal, then follow the on-screen instructions to install Rust.
curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh
I started following the hello world tutorial to set up the system and it wasn’t all plain sailing. For one I probably should have done the install as a normal user but after mucking around with environment variables and permissions into the root user directories it was just easier to do the work as root. (I know rolling over in my grave). So it wasn’t all plain sailing – and once I got to the compile stage there were a few basic compiling tools that the RHWSL needed. This is how it went:
[root@Venom RHWSL]# mkdir rustProgramming [root@Venom RHWSL]# cd rustProgramming/ [root@Venom rustProgramming]# cargo new hello_world Created binary (application) hello_world package [root@Venom rustProgramming]# [root@Venom rustProgramming]# ls -ltr total 0 drwxrwxrwx 1 root root 4096 Feb 8 18:27 hello_world [root@Venom rustProgramming]# [root@Venom rustProgramming]# cd hello_world/ [root@Venom hello_world]# ls -ltr total 0 -rwxrwxrwx 1 root root 180 Feb 8 18:27 Cargo.toml drwxrwxrwx 1 root root 4096 Feb 8 18:27 src
[root@Venom hello_world]# cat Cargo.toml [package] name = “hello_world” version = “0.1.0” edition = “2021” See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[root@Venom hello_world]# cargo build Compiling hello_world v0.1.0 (/mnt/d/RedHat/RHWSL/rustProgramming/hello_world) error: linker cc not found | = note: No such file or directory (os error 2) error: could not compile hello_world due to previous error
Bingo – first problem – no compiler. I installed make and gcc – not really sure if I needed make but figured it would be a nice to have anyway.
[root@Venom hello_world]# which make /usr/bin/which: no make in (/root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/Program Files (x86)/Common Files/Oracle/Java/javapath:/mnt/c/Program Files (x86)/ ……….lots more here
[root@Venom hello_world]# which cc /usr/bin/which: no cc in (/root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/Program Files (x86)/Common Files/Oracle/Java/javapath:/mnt/c/Program Files (x86)/ ……….lots more here
[root@Venom hello_world]# yum install make …Dependencies resolved. Package Architecture Version Repository Size Installing: make x86_64 1:4.2.1-10.el8 ubi-8-baseos 498 k …. more in here you don’t need to see … Installed: make-1:4.2.1-10.el8.x86_64 Complete!
[root@Venom hello_world]# which make /usr/bin/make
[root@Venom hello_world]# yum install gcc …Dependencies resolved …. more in here you don’t need to see … Installing dependencies: binutils x86_64 2.30-108.el8_5.1 ubi-8-baseos 5.8 M cpp x86_64 8.5.0-4.el8_5 ubi-8-appstream 10 M glibc-devel x86_64 2.28-164.el8 ubi-8-baseos 1.0 M glibc-headers x86_64 2.28-164.el8 ubi-8-baseos 480 k …Transaction Summary Install 14 Packages Total download size: 51 M Installed size: 123 M Installed: Complete!
[root@Venom hello_world]# which cc /usr/bin/cc
[root@Venom hello_world]# cargo build Compiling hello_world v0.1.0 (/mnt/d/RedHat/RHWSL/rustProgramming/hello_world) Finished dev [unoptimized + debuginfo] target(s) in 2.09s
[root@Venom hello_world]# ls -ltr total 0 -rwxrwxrwx 1 root root 180 Feb 8 18:27 Cargo.toml drwxrwxrwx 1 root root 4096 Feb 8 18:27 src -rwxrwxrwx 1 root root 155 Feb 8 18:28 Cargo.lock drwxrwxrwx 1 root root 4096 Feb 8 18:28 target
This is more concept art for a protagonist in the new project called The Gap. Okapi Boy – I have always loved Okapi’s. They are shy and remained hidden from the world in the deepest parts of the jungle until they were discovered in the 50’s. Beautiful Zebra like patterns on skin like a Deer. This is a deeply peaceful animal with large soulful eyes. He dreams and offers advice but you have to seek him out and finding him is not always easy or straightforward. In The Gap you get the chance to play as the Okapi for part of the story.
Okapi Boy
There used to be a great example of this rare creature in the Melbourne Museum Taxidermy collection (which I used to love but has now sadly been taken off the exhibit floor). You can take a virtual tour of the old “Wild” exhibition here.
I have a LOT more concept art to do as I want to get a very clear picture of my characters before I start modelling.
I also stared working on the programming for the game. One element I want to include in the story is a “mini-game” called Fox and Geese. It’s played on a chess board and was one of the first games I remember my dad teaching me how to play. It’s pretty straightforward and uses a chess board and pawns. Four white pawns are the Geese who can only move forward and diagonally one row at a time. The Fox is a black pawn that can move diagonally one space at a time in any direction. Moves are taken in turn and the aim of the game is for either the Fox to break the line of Geese or the Geese to “capture” the Fox so that he cannot move.
Fox And Geese
It’s early days yet with only the board set up and pawn selection scripts started but it was actually pretty fun setting up the programming that took care of the board. When you learn programming you get to do lots of arrays and nothing screams “ARRAY” like a chess board. So it’s kind of nice to use one of those basic skills that you use in all programming in such a nice simple example. You get to add a little interest to the basic array with the inclusion of alternating colours. Here is the code below:
using UnityEngine;
public class FnG_MakeBoard : MonoBehaviour
{
public GameObject boardPiece;
public Vector3[] positions;
// Start is called before the first frame update
void Start()
{
positions = new Vector3[64];
float start_x_pos = -1.75f;
float start_y_pos = -1.75f;
float rowcount = 0f;
float linecount = 0f;
for (int i = 0; i < positions.Length; i++)
{
positions[i] = new Vector3(start_x_pos, start_y_pos, 0f);
start_x_pos += 0.5f;
rowcount += 1;
if (rowcount == 8)
{
start_x_pos = -1.75f;
start_y_pos += 0.5f;
rowcount = 0f;
linecount++;
}
Instantiate(boardPiece, positions[i], Quaternion.identity);
Color32 myRed = new Color32(185, 48, 48, 255);
Color32 myGray = new Color32(106, 106, 106, 255);
if (i % 2 == 0)
{
if (linecount % 2 == 0)
{
boardPiece.GetComponent<SpriteRenderer>().color = myGray;
}
if (linecount % 2 != 0)
{
boardPiece.GetComponent<SpriteRenderer>().color = myRed;
}
}
if (i % 2 != 0)
{
if (linecount % 2 == 0)
{
boardPiece.GetComponent<SpriteRenderer>().color = myRed;
}
if (linecount % 2 != 0)
{
boardPiece.GetComponent<SpriteRenderer>().color = myGray;
}
}
}
}
It’s really nice to get a game finally published. Endless Elevator is now on the Google Play Store so I’ve been planning and experimenting and just plain playing around with Unity and Game Ideas before I start a new project. It’s kind of like a holiday.
I like maths and I also like visualising it with Unity. This little project is one of the simplest types of maths that is generally associated with game programming and that’s adding and subtracting vectors and getting the distance between two vectors.
These sorts of functions are endlessly useful. They answer questions like: How far away is my target? Is my Player going to fall off the platform? Can I jump between two platforms? And so on and so on.
What I like about this one is that it highlights two things. 1. The difference between a player and a target’s perspective (in this project I nominate the circle as the player and the cylinder as the target), and 2. calculating the magnitude or distance between them.
To calculate the distance (magnitude) between two points you take the square root of the square of the x and y values. Which sounds a little complicated but is really just making a square the size and length of your x, and adding that to a square the size of your y, and then getting the square root of the total size of that square.
This is how it looks in the Scene View:
How it works
The project is really easy to set up. All you need is an Empty Game Object to hold the script and two other objects. I used a Capsule and a Sphere. Drag the two objects into the public Transform components on the script.
The Project Hierarchy and Properties of the Script
Here is the reproduced script below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.UI;
public class GridGizmo : MonoBehaviour
{
public Transform circle;
public Transform capsule;
public Vector3 differenceToCapsule;
public Vector3 differenceToCircle;
public float magnitude;
void Update()
{
differenceToCapsule = capsule.position - circle.position;
differenceToCircle = circle.position - capsule.position;
/*
Magnitude of a Vector (Length of the Vector)
We use Pythagoras' theorem to calculate it:
|a| = √( x2 + y2 )
*/
magnitude = Mathf.Sqrt(Mathf.Pow(differenceToCapsule.x, 2) + Mathf.Pow(differenceToCapsule.y, 2));
}
public void OnDrawGizmos()
{
// Grid Number Labels
for (int i = -10; i < 11; i++)
{
UnityEditor.Handles.Label(new Vector3(0f, i, 0f), "" + i);
}
for (int i = -20; i < 21; i++)
{
UnityEditor.Handles.Label(new Vector3(i, 0f, 0f), "" + i);
}
// Labels of Objects
UnityEditor.Handles.Label(new Vector3(circle.position.x + 1, circle.position.y, circle.position.z), "POSI: " + circle.transform.position);
UnityEditor.Handles.Label(new Vector3(circle.position.x + 1, circle.position.y - 0.5f, circle.position.z), "DIFF: " + differenceToCapsule);
UnityEditor.Handles.Label(new Vector3(circle.position.x + 1, circle.position.y - 1f, circle.position.z), "Magnitude: " + magnitude);
UnityEditor.Handles.Label(new Vector3(capsule.position.x + 1, capsule.position.y, capsule.transform.position.z), "POSI: " + capsule.transform.position);
UnityEditor.Handles.Label(new Vector3(capsule.position.x + 1, capsule.position.y - 0.5f, capsule.transform.position.z), "DIFF: " + differenceToCircle);
// The line
Gizmos.color = Color.blue;
Gizmos.DrawLine(circle.position, capsule.position);
}
}
As I mentioned above I do like mathy things in Unity. Here are some of the other one’s I’ve done which are a bit more complicated. I find them really really useful to cement those ideas that I should know back to front but always forget exactly how they work.
My son asked to do some game programming with me last week. I was super excited. We did an eyeball rolling around in a circular motion (see image below). He did everything from working in Unity to making the assets and materials and I was very proud. I helped with the function to make it circle. It got me thinking how to explain circular movement and the use of Pythagorean triangles and cos and sin functions. They are simple maths but kind of hard to explain without a picture so hence this project about making circles with triangles.
Rolling Eyeballs!
The Basic Idea
To simplify I’ll work in 2D so we only got two Axis. X and Y. The Center of our circle will be on the 0 (zero, zero). So we could think of this as a co-ordinate system with two planes. If we were mathematicians we would call this a Cartesian Plane after Rene Descartes but being a Game Developer with Unity I’m going to call it the Scene view in 2D.
Our circle is defined by it’s Radius (ie. the distance from the Center).
On each Update() event our GameObject gets a new X,Y position on the Radius of the circle.
The Angle of Change
To start with we work out what the angle of difference is between this Update and the last one. The time passed during that period is known as the DeltaTime. Our “speed” is how many Radians we are travelling around the circle in that time. A radian is the length of the radius laid around the circumference of the circle. We multiply the Speed (how fast we are ticking through the radians) by the DeltaTime (time passed since last Update) to tell us that angle size.
# angleInRadians += RotateSpeed * Time.deltaTime;
Convert Polar to Cartesian
After working out the angle we have what is called a Polar Coordinate. That is we define our location by how far away it is (the distance – in this case as it’s a circle it’s always the same i.e. the radius), and what the angle (θ) is. Now we need to convert between that definition of a location in the Scene View to another one we can use in the Unity move function.
This is where Pythagoras and right angled triangles comes in – to convert from Polar Coordinates (r,θ) to Cartesian Coordinates (x,y) we use cos and sin : x = r × cos( θ ) y = r × sin( θ )
In Unity there is already a function that does this:
newPosition = new Vector2(Mathf.Cos(angleInRadians), Mathf.Sin(angleInRadians)) * Radius;
But what does this really mean? Cos and Sin are just numbers. They represent the relationship between two sides of the triangle.
For example cos is the value of the relationship between the side of the triangle that is adjacent to the angle (the side next to the angle) and the hypotenuse (the long side on the other side of the angle). We are going to use cos to find the x value (how far along the horizon it is) in our Vector2 position. The same way we use Sin to find the y position (how far up or down).
In the image below see how there are squares built off each side of the triangle. Pythagorean theory for right angled triangles states that the area/volume of the two squares built of the two smaller sides will be equal to the area/volume of the big square built off the longest (hypotenuse) side of the triangle.
The big blue square has a volume of 1 because the radius of the circle is 1 unit on the Cartesian plane and One Squared is One. The volume of the other two squares (Magenta and Yellow) will always add up to one. Their volumes added together will always equal the volume of the blue square. This is the Pythagorean theory of right angled triangles in action. The length of a side of the Yellow box is the x value and the length of a side on the Magenta box is the y value. That’s our current position in (x, y) format which we can pass to the transform.position function.
The two images below show two Updates() that are reasonably close together so you can see in detail and in “freeze frame” what’s going on between the Updates() with all the variables. You can see the angle go from about 8 degrees to 20 degrees and the changing values for sin and cos which result in changing (x,y) values and volumes of the squares.
One Position on Update()The next position on Update()
That’s basically it apart from some modifications to the values for being on the negative sides of the circle.
Hi Trixie here. It’s Code Review time! Yay. Time to clean up that code and start really looking at all the crap work you’ve done over the last months and clean that sh!t up!
I’ll post our in-house Style Guide (which of course goes out the window when you are coding in anger) at the bottom of the post. Hopefully someone else will follow it or find it useful.
This is how the code review works:
Make a list of all your scripts and the game objects they are attached to.
Go through all the variables/functions/iterators and make them follow the standard in the Style Guide (which includes making logical names sensible to humans).
Check all your Public references and make Private if you are really not using them.
Trawl the console output and clean up the extraneous debugging guff.
Start grouping your code into functions that do a similar thing and try and make them standardised.
If you are lucky you will find some optimisations in there as well to make your game run faster, better, more efficient, lighter.
Then at least it will be another few months before you muck it up again.
I know there are automated tools to do some of this work but I prefer to work though my own methods. I like to have complete control over the process. My favourite tool for analysis is the Unix Command line (I know weird right?). My workstation is Windows 10 but I have a Cygwin like utility called MobaXterm installed which allows me to interrogate the file system like a Unix machine and use grep and other commands on all the files that make up my scripts.
Basically I want to build a big spreadsheet of information that lists all my scripts and the Game Objects they attach to and the Public interfaces and variables etc.
This is one that I started for Endless Elevator:
Then I start extracting Game Objects and Code loops using the Unix Command Line so I can start to build a picture of what’s going on.
One of the outcomes of this process is that I want to be left with a map of how stuff works that I can connect the dots on in the future. It’s kind of like a Design Document in reverse.
Here are a few examples of the commands I’m using and how the information is extracted:
# grep bool *.cs # Getting out all my bools – I tend to use true or false tests a lot to explicitly define functions and events.
As you can see above the bool names are mostly descriptive and the variable name is mostly in camelCase. Those few like swingme will get updated to swingMe and the overly generic “yes” and “yesNow” (what was I thinking!) will be made more descriptive of what we are really agreeing to in that code.
This command looks at all the sort of functions I am using and where. It’s nice to know which one’s don’t have an Update() function and where all my Collisions and Triggers are.
# egrep “{|}” AI.cs # I use something like this to collect an idea of the complexity of my loops and functions.
# egrep “{|}(|)|if|for|else” AI.cs # This is my favourite type of command. Like the command above I use it for working out the structure of a script. This is way easier than scrolling through lines and lines of code and comment and makes it really easy to spot areas where you have gone loopy crazy pants.
# grep GameObject *.cs # This one makes a good list of all the scripts that I reference another Game Object in. It helps build a visual map of what dependencies there are between objects.
# grep script *.cs # This one does a similar thing in that it grabs all the times that I am calling something from within another script attached to another Game Object..
The command line is also a nice quick way to check how many lines of code you have written.
As a basic guide though I always try and mimic the Unity API manual. For example this page for OnCollisionEnter:
// A grenade
// - instantiates an explosion Prefab when hitting a surface
// - then destroys itself
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
public Transform explosionPrefab;
void OnCollisionEnter(Collision collision)
{
ContactPoint contact = collision.contacts[0];
Quaternion rotation = Quaternion.FromToRotation(Vector3.up, contact.normal);
Vector3 position = contact.point;
Instantiate(explosionPrefab, position, rotation);
Destroy(gameObject);
}
}
The comments are appropriate. The Class name uses a Capital for each word. The curly brackets are on a new line and indented block space. Varables are in camelCase.
Here is our minimal Style Guide. This is the basics of how we roll things: