Godot is an astonishingly feature packed development framework for creating games and graphical simulations. It can do fancy 3d modelling and physics as well as 2d and GUI elements. I am particularly impressed that the GUI of Godot itself is actually created with its own technology (presumably you could make a game that has an in-game creation feature using Godot). It is free, open source, Linux friendly, and pretty much completely wholesome.
Resources
-
J Bullock makes some nice 3d Godot3 tutorials
-
KidsCanCode is good for Godot tutorials (don’t let the name fool you).
-
Free assets
-
Simple Pong Demo. This little example is a good starting point because it is a top down 2d without gravity suitable for map style vehicle simulations.
-
A very quick demo getting right to the point of making a topdown game in 7 minutes.
-
A very competent video series by GDquest.
-
Godot is great but if you’re interested in open source game engines O3DE — the Open 3d Engine — is worth taking a look at.
Installation
Godot’s installation is nothing short of a miracle. You go to their web site, you get a compressed file (alas zipped) and uncompress it.
The download is astonishingly less than 20MB! It is a single 44MB executable at full size (at release of Godot3 in January 2018). This is shockingly compact and easy to deal with but it continues to impress: to run it, just execute the file and it’s running!
A good place to start is by clicking "AssetLib" which brings up all the official demos into the GUI of the editor. You can select one and download it and start playing with it. It’s actually a very clever way to teach the system.
Vim and Command Line
It looks like you can just edit the files in the project directories. The trick is how to synchronize them. You can just click on the file name in the editor to refresh. Sort of. I’d like to find a way to just not have it do any auto-syncing and do it manually.
How to use the command line to work with Godot.
Syntax highlighting was ok by default as a conf file. Install a proper syntax highlighting by doing this.
mkdir ~/.vim/syntax ; cd ~/.vim/syntax
wget https://raw.githubusercontent.com/quabug/vim-gdscript/master/syntax/gdscript.vim
Maybe add this to your ~/.vimrc
.
au BufRead,BufNewFile *.gd set filetype=gdscript
Tabs
The editor likes to use tab indentation, but it’s easy to fix.
Editor→ Editor Settings → General → Text Editor → Indent → Type
Change from evil "Tabs" to "Spaces".
Editing
-
RMB - aims camera angle from its current position
-
LMB - selects
-
[S]-MMB - pan view (translate), reposition view in display or, as I like to think of it, "shift" the view (same in Blender)
-
[C]-MMB - scale view, zoom/move view/camera closer, vertical mouse axis only (similar in Blender)
-
[S]-F1 - Contextual help for highlighted name in script editor.
Ontology
A "Scene" is a tree of "Nodes".
A Node may have these kinds of features.
-
Name
-
Properties
-
Callback
-
Extendable - You can make the actual functionality different
Physics
-
Static body - Sprite/model + collision box
-
Rigid body - Do not control position explicitly. Apply forces (gravity,
add_force()
,apply_impulse()
, etc). -
CollisionObject2D
-
PhysicsBody2D
-
StaticBody2D - Efficient for things that don’t move (walls, ground, the "platform")
-
RididBody2D - Realistic simulated physics (gravity, etc)
-
-
Rigid mode - Moves in response to physics forces.
-
Static mode - Behave like a static body (won’t move)
-
Character mode - similar to Rigid but no rotation
-
Kinematic - Use code for moving like KinematicBody2D.
-
KinematicBody2D - Arcade, moves but no normal physics necessarily
-
-
Area2D - Detects object overlapping
-
Can set gravity to 0 in "Physics 2d" in Property Settings for 2d top down simulations (think Asteroids).
apply_impulse()
is more abrupt than add_force()
Example of how to insert physics nodes.
Scene tab → + → Spatial → CollisionObject → PhysicsBody → RigidBody
Area2D - Sprite - add your artwork - CollisionObject2D - (don’t drag collision shapes from outside box, use blue inner box handles).
KinematicBody2D - Arcade, moves but no normal physics necessarily
Use move(Vector2 real_vec)
method to move it to return "remaining
motion" left after a hit. Allows for a n.reflect(remaning)
or a
n.slide(remaining)
.
- Sprite
- CollisionShape2D
How to directly do metaphysical things with a physics object like a PhysicsBody2D. For example, you may have a space ship that reappears on the opposite side of the screen when it goes off the screen (i.e. torus). Usually the outcome of where the ship is positioned is calculated solely by the physics engine. But here’s how to reach in and make adjustments for such a situation.
# Taken from: https://www.youtube.com/watch?v=xsAyx2r1bQU
func _integrate_forces(state):
set_applied_force(thrust.rotated(rotation))
set_applied_torque(rotation_dir*spin_thrust)
var xform= state.get_transform()
if xform.origin.x > screensize.x:
xform.origin.x= 0
if xform.origin.x < 0:
xform.origin.x= screensize.x
if xform.origin.y > screensize.y:
xform.origin.y= 0
if xform.origin.y < 0:
xform.origin.y= screensize.y
state.set_transform(xform)
Also custom_integrator()
looks interesting too to make your own
physics systems.
set_fixed_process(true)
- Force physics to 1/60 second (or whatever).
Tween
A Tween
node is just a way to interpolate a continuous state
transition function. Here is a nice display of what that means:
http://easings.net/
bool interpolate_property(object,property,initialvalue,endingvalue)
GDScript
-
#
Comments as usual -
$
= alias forget_node()
. For example, you can refer to$AnimatedSprite/Monster.local_coords
. Alsohas_node()
. -
func
= Python’sdef
, though don’t useself
in the definition (althoughself
may be available in the functions). Functions aren’t proper types and can’t be stored in variables, etc.static func myspecialfn(x,y):
can be used to make sure that x and y are not inherited from elsewhere. -
Native vector types
Vector2(x,y)
andVector3(x,y,z)
-
Single or double quotes like Python.
-
Dictionaries and lists (perhaps called arrays) seem just like Python. e.g.
var moves={"right":Vector2(1,0),"left":Vector2(-1,0)}
Alsomydict.keys()
works. -
null
is a type that can not be assigned values. Hmm. -
Other types
bool
,int
,float
,String
,Rect2
,Transform2D
(transform matrix type),Plane
,Quat
(quaternion),AABB
(axis aligned bounding box),Basis
(3x3 matrix for 3d transforms),Transform
(the 3d version),Color
,NodePath
,RID
(resource id),Object
(base class for user created types) -
enum MOB_BAD, MOB_GOOD=10, MOB_UGLY
is the same asconst MOB_BAD=0
,const MOB_GOOD=10
, andconst MOB_UGLY=11
. -
const e= 2.71828182845904523536
-
Boolean keywords are
true
andfalse
, not the Python capitalized ones. -
Random numbers
-
randi()
- random integer, best used with a modolo. -
rand_range(minval,maxval)
-
-
print("Shows up in the console.")
-
clamp(x,min,max)
- set x to be x or min or max if x is lower or higher than min and max respectively. -
class MyClass
and then instantiate it withvar mc= MyClass.new()
and use with something likeprint(mc.mymember)
; also,extends MyClass
for inheritance -
Keywords that seem mostly identical to Python:
if
,elif, `else
,for
,while
,break
,continue
,pass
,return
,range
-
match
is like switch in other languages; note that break is default, use continue to fall throughmatch mycoin: true: print("Heads!") false: print("Tails!")
-
~
Bitwise not,<<
bitshifting, [&
,|
] bitwise [and,or] -
||
oror
boolean or. Same with and. -
0xCECE
Hex numbers -
"""multi line string"""
Like Python. -
@"NodePath"
- I’d like a better example of this
Any variable you want exposed in the editor use
export var speed= 55
-
Vector2(x,y)
Some typical expected functions for node scripts (classes).
-
func _ready()
- Called every time the node is added to the scene. Often contains the lineset_fixed_process(true)
-
func _process(delta)
- Called every frame where node is relevant.
delta
is how much time has elapsed since last frame; used for frame
rate independence.
randomize()
- set random seed with something really random. Or
something specific?
If you have a "scene" that describes how some game element works, you
can instantiate many objects of that class. Here a parent "Node" (just
a simple plain Node
type node) spawns 10 animated sprites.
extends Node
onready var sprite= preload("res://Sprite.tscn")
func _ready():
for i in range(10):
var s=sprite.instance()
add_child(s)
UI
It seems common to start by defining your own input map entries. Start
your new project then go to Project → Project Settings → Input Map
tab, and enter move_up
, move_left
etc in the Action box. Then go
to the plus on each and Add Key to add a binding (A,S,D,W, or better,
k,h,j,l etc).
I think the predefined ones (e.g. ui_up
, ui_right
, etc) are there
to support the UI (menus, etc) keybindings.
Here’s a typical control scheme for mapping arrow keys (or WASD if it’s remapped) to do the sensible thing.
func get_input():
thrust= Vector()
if Input.is_action_pressed("ui_up"):
thrust= Vector2(engine_thrust,0)
if Input.is_action_pressed("ui_down"):
thrust= Vector2(-engine_thrust,0)
if Input.is_action_pressed("ui_right"):
rotation_dir += 1
if Input.is_action_pressed("ui_right"):
rotation_dir -= 1
Here’s another possibly better way to do the same thing.
export var speed=100
var vel= Vector2()
func _read():
set_fixed_process(true)
set_pos(Vector(500,300)
func _fixed_process(delta):
var input= Vector2(0,0)
input.x= Input.is_action_pressed("ui_right") - Input.is_action_pressed("ui_left")
input.y= Input.is_action_pressed("ui_down") - Input.is_action_pressed("ui_up")
vel= input.normalized() * speed
set_pos(get_pos() + vel * delta)
To have a complex UI display in-game you can use lots of different container nodes. This video does a very good job introducing that concept.
Nodes
Node2D is a good choice for a root node in a 2d project.
StaticBody2D is a good parent wrapper of a Sprite node for things like backgrounds.
Quit
A normal thing to want to do, especially during development, is to politely quit your game. I defined the escape button using the same menu as previously shown and then added this.
if Input.is_action_just_pressed("quit_button"):
get_tree().quit()
Signals
_on_some_action
- Godot naming convention for responding to signals.
signal item_grabbed # Alerts interested funcs that an item was grabbed.
func on_item_area_enter(area): # Run if item collision occurs.
emit_signal("item_grabbed") # Alert other nodes.
queue_free() # Makes this object disappear from entire process.
func spawn_item():
var g= item.instance()
item_container.add_child(g)
g.connect("item_grabbed",self,"_on_item_grabbed")
func _on_item_grabbed():
itemcount+=1
Performance
Need to audit the frame rate? That and many other interesting
performance metrics can be studied with the get_monitor
function
described
here.
C++
Of course you can use C++ to strong arm the engine itself. http://docs.godotengine.org/en/latest/development/cpp/ Quite an advanced topic. It would definitely require a lot of knowledge about the engine in general.