Player
Replaced the ball with a real character model. At the moment I have just a few character models with two animations (run and idle): Video
The character was made with MakeHuman and the animations are from Mixamo. The workflow is straight forward and (more or less) painless:
- In MakeHuman export the character as .mhx2.
- Import in Blender. The MakeHuman Blender Plugin has a MHX2 importer.
- If needed apply transformation, but if you use MHX2 i think this is not needed.
- Export from Blender as .fbx.
- Upload the FBX file to Mixamo.
- Select the animation.
- Download the FBX file.
- Import in Blender.
- Export the character with the Urho3D-Blender plugin. I would recommend to export a Prefab so you can load the whole thing with materials etc. at once.
NPCs
RPGs (and other games) usually have NPCs - non playing characters, well they are playing but they are computer controlled.
This game should be largely scriptable. There are different game formats and maps and each should just differ in the script that is executed.
A game script could look like this:
function onStart()
-- self is the this pointer
-- Spawn an NPC and set the position.
local smith = self:AddNpc("/scripts/creatures/npcs/smith.lua")
if (smith ~= nil) then
local x = -6.71275
local z = 12.5906
local y = self:GetTerrainHeight(x, z)
smith:SetPosition(x, y, z)
-- Here we just use the Y-axis, internally it uses Quaternions
smith:SetRotation(180)
end
local merchant = self:AddNpc("/scripts/creatures/npcs/merchant.lua")
if (merchant ~= nil) then
local x = 4.92965
local z = 12.2049
local y = self:GetTerrainHeight(x, z)
merchant:SetPosition(x, y, z)
merchant:SetRotation(180)
end
end
function onStop()
end
function onAddObject(object)
print("Object added: " .. object:GetName())
end
function onRemoveObject(object)
print("Object removed: " .. object:GetName())
end
function onPlayerJoin(player)
player:AddEffect(empty, 1000, 0)
print("Player joined: " .. player:GetName())
end
function onPlayerLeave(player)
player:RemoveEffect(1000)
print("Player left: " .. player:GetName())
end
-- Game Update
function onUpdate(timeElapsed)
-- print(timeElapsed)
end
As with other scripting APIs, there are events that are called by the program (from C++), and there are objects that have methods accessible by the script.
An NPC script is as simple:
-- Object stats/properties
name = "Smith"
level = 20
modelIndex = 5 -- Smith body model
sex = 2 -- Male
creatureState = 1 -- Idle
prof1Index = 1 -- Warrior
prof2Index = 0 -- None
function onInit()
return true
end
function onUpdate(timeElapsed)
-- Do something intelligent!
end
-- self was selected by creature
function onSelected(creature)
print(creature:GetName() .. " selected me, the " .. self:GetName() .. " :D")
end
-- creature collides with self
function onCollide(creature)
-- Testing Octree query
local objects = self:QueryObjects(2.0)
print(type(objects))
for i, v in ipairs(objects) do
print(i, v, v:GetName())
end
end
Navigation
Navigation is a difficult topic. Fortunately there is Recast & Detour, a library to find paths with a navigation mesh. Currently it can to go to a static position and follow a moving object. Technically it's the same, but it doesn't recalculate the path when the object is not moving. However, I've read somewhere that Detour can calculate incomplete paths which would be useful for following objects, but I didn't figure that out yet.
Detour uses navigation meshes created with Recast. The image bellow shows the generated navigation mesh based on the heightfield of this map.
As mentioned earlier, the heightfield is just a bitmap where brighter colors are higher altitudes than darker colors. A mesh is created with a custom command line program from this bitmap, which Recast uses to generate the navigation mesh.
To be continued...