Below you’ll find a consolidateD and structured guide that will help you start building up your own game.
Have fun and be creative!
Have fun and be creative!
While Visionaire is a really powerful engine designed originally for 2D point ‘n click adventure games, it is possible since version 5 with the addition of Box2D and the Ilios scripting language to create different genre games, like physics-based platformers. So, welcome to the first Box2d/Ilios scripting tutorial for Visionaire! Let’s try to make a prototype platformer and see how this works 🙂
Some useful reference links:
https://box2d.org/documentation (Box2D)
https://www.visionaire-studio.com/luadocs/ilios.html (Ilios)
Feel free to contact us for your comments, ideas and any assistance to your projects! As always, the community’s assistance is also there to help:
https://www.visionaire-studio.net/forum (Forum)
https://discord.gg/feQkHgNe (Discord)
You may download the complete (so far) Visionaire project from here:
sunny-land.zip | Sunny Land Visionaire Prototype |
Implemented: | Character Movement (Keyboard) |
Collectibles (Gems) | |
Pending: | Scrolling |
Enemies | |
For this tutorial, we will use:
Please note that while programming knowledge is not critical, it is important in order to fully understand the concepts set out in this tutorial. An in-depth of these concepts will not be covered here.
Currently tiling is not supported in Visionaire so we will use a set scene for the background which includes all layers. There is such an image in assets\PNG\environment\environment-preview.png:
Although it’s not necessary for the plaformer, Visionaire needs a character in order for the game to run, so let’s add one:
And ofcourse don’t forget to assign it as the active character in Game Properties:
The background we have defined above is 1456x464px but the view area will be much less, let’s set this to 320 x 240 under Game Properties.
On the same menu, make sure you activate the pixel effect to have a crispy look on the pixel art:
Important note! Depending on your monitor and desktop resolution, 320 x 240 might be quite small. There is a way though to change the resolution window at the game’s Start Action, for this tutorial we have used a 6x magnification factor:
Action areas are sections you can draw in and can be used to determine if the engine should perform some actions when the specified – or any – character walks into or out of the action area. You can consider them as triggers. A few example uses:
You can create one or more action areas in a scene similarly to Way Borders:
Next step is to draw the action areas. We have to create closed polygons, within which our actions will be triggered (or not)
Final step is to define the relevant actions to be triggered:
Action Area Event Handler
You may also register an event handler to listen out for any triggers:
function onActionArea(movement, actionArea, character) if movement == "ENTER" then -- do something elseif movement == "LEAVE" then -- do something else end end registerEventHandler("actionArea", "onActionArea")
Tip: actionArea == ActionAreas[“example”] (ActionAreas is a table)
Another option would be to fade the scene brightness instead of using a black background, using the to() tweening function:
game.CurrentScene:to(500, {SceneBrightness = 0}, easeQuintOut) -- easing is optional
Visionaire offers scrolling effects for your scenes that are larger than the viewable area along with the possibility to adjust various settings.
Adjust the scroll speed
The basic setting for the scroll speed of your scenes (in pixels / sec) is done through Game Properties:
For a more dynamic adjustment, e.g. if you need to adjust this setting during gameplay, you can use lua as follows:
game.ScrollSpeed = 300 -- scroll by 300 pixels a second
Snap the scroll position of a scene
You can move the camera at any point (this will be top left corner of the camera view) of your current scene using the relevant action part:
or with Lua:
game.ScrollPosition = {x = 300, y = 200} -- important note: x, y is the top left corner of the scroll position!
You can also snap the camera position to a specific object with the relevant action part:
Wait until scrolling finishes
Sometimes you need to wait for scrolling to finish before letting some actions continue. You can achieve this by detecting if the position of game.ScrollPosition changed over the last frame in a main loop: (set as definition script)
local lastScrollPosX = 0 function scrolling() if lastScrollPosX ~= game.ScrollPosition.x then Conditions["scrolling"].Value = true -- create a condition 'scrolling' somewhere lastScrollPosX = game.ScrollPosition.x else Conditions["scrolling"].Value = false end end registerEventHandler("mainLoop","scrolling")
You can now use the action part ‘Wait until ‘scrolling’ is false to control your actions flow.
Smooth Scrolling
To start the scrolling smoother (i.e. in the beginning and end of the scrolling), from the Game Properties:
Adjust scrolling triggering by the character
Visionaire uses bounding boxes at the scene edges to monitor when the character goes over them so that the scrolling can be started. These bounding boxes can be adjusted for both the horizontal and vertical axis using the relevant action parts:
Alternatively, with Lua:
game.HorizontalScrollDistance = 300 -- scroll on X axis when character is 300 pixels or less from scene edge (left or right) game.VerticalScrollDistance = 150 -- scroll on Y axis when character is 150 pixels or less from scene edge (up or down)
For example, to keep the character always at the center of the screen when scrolling horizontally, you can use:
game.HorizontalScrollDistance = game.WindowResolution.x / 2 -- always using 50% of the current window width size
Scroll Scene with the Mouse
By default, you cannot make a scene scroll by hovering the mouse over its edges, but you can enable this in the scene’s properties:
Alternatively, with Lua:
game.CurrentScene.ScrollOnEdges = true
Using Lua, you can also adjust the bounding box size which will trigger the scroll when you hover the mouse over it:
game.CursorHorizontalScrollDistance = 50 -- scroll on X axis when cursor is 50 pixels or less from scene edge (left or right) game.CursorVerticalScrollDistance = 200 -- scroll on Y axis when cursor is 200 pixels or less from scene edge (up or down)
Limit Scrollable Area
By default, a scene can be scrolled from edge to edge. You can limit this by defining a specific rectangle for the scrollable area:
game.CurrentScene.ScrollableArea = {0, 0, 1920, 1080} -- {x, y, width, height}
Parallax Scrolling
Apart from the scene’s background which always scrolls in the defined scrolling speed, you can set independent scrolling speed for objects to give a nice parallax scrolling effect. This can be applied to any scene object via its effects tab for both axis x and y:
100% will use the defined scene scroll speed while any value <100% will make it slower and 0% will stop it. You can also use Lua to adjust these:
Objects["table"].ScrollFactorX = 50 Objects["table"].ScrollFactorY = 100
There are 3 types of curves:
Note: the first point of the curve is the starting point (also for the continuous curve)
With Tween Curves you can move a particle system (through an object) on a curve and rotate, or make non-linear animations like flying/swooping bird or wave motion etc.
Step 1 – Create a Curve in a specific scene. Each curve has an index: 1,2,3…
Example 1 – Move a Particle System (through an object)
Step 2.1 – Create a scene object & call it e.g. “moving”
Step 3.1 – Create a particle & link it to the scene object.
Now let’s write a tween loop to make it move around the curve we have created:
startCallBackTween(duration, function(position) --code-- end, easing, loop, pendulum) duration: the time needed to complete 1 loop loop: true = infinite loops, false = 1 loop only pendulum: if loop = true, it will reverse direction each next loop startCallbackTween(30000, function(x) -- starts loop function that lasts for x time local pos = game.CurrentScene.Curves[3]:curveAt(x) -- gets current position of linked curve local direction = game.CurrentScene.Curves[3]:curveDirection(x) - 1.57 -- gets the current direction based on position of curve in the curve local p = graphics.getParticles(game.CurrentScene.Objects.moving) -- gets the particle belonging to the scene object ["moving"] p.emissionDirection = {0.0, direction, 0.0, direction} -- updates the angle of the particle p.center = {pos.x,pos.y} -- updates the position of the particle end, easeLinearIn, true, false)
Example 2 (Move Animation)
startCallbackTween(30000, function(x) local pos = game.CurrentScene.Curves[3]:curveAt(x) local direction = game.CurrentScene.Curves[3]:curveDirection(x) - 1.57 ActiveAnimations["animation_name"].AnimationCurrentPosition = {x = pos.x, y = pos.y} end, easeLinearIn, true, false)
You can create curves in each scene. ‘Curves’ is a linklist; you can access a specific curve in the list using the index, in the above example 3.
The curve object has 3 functions:
curveAt(x) -> position
curveDirection(x) -> degree (rad direction)
curveDerivative(x) -> vector pointing in the direction (tangent)
How to stop curve
If the function(x) returns false, then the startCallbackTween() will stop. Taking into consideration that if you exit a scene that has curves used for animation you will get an error as it will not be able to find the curve or the active animations anymore, use the following format:
startCallbackTween(duration, function(x) if game.CurrentScene == Scenes["Scene that the curves/anims live"] then -- do your stuff else return false end -- it will kill the loop in any other scene (returns false) end, easeLinearIn, true, false)
Lightmaps allow characters and objects to change brightness or tinting depending on where they are located in the scene so that they follow the light sources in the scene.
Lightmaps maps work based on character / object positions in the scene. Basically the engine:
Notes:
Especially for objects, you can control which objects will be affected by the lightmap and which not:
Objects["apple"].LightmapAffected = true
Lightmaps are the easiest way to apply tinting, nevertheless you can also use Lua:
Characters["Dragon"].Tint = 0xFF0000 -- format used by Visionaire is 0xBGR (BBGGRR) so this will tint the character blue
Alternatively you could use shaders to directly apply lighting to your scenes which would affect things in a more dynamic way.
A nice built in effect to use in your game is the earthquake effect, to simulate such events in your game. You may do so with the ‘Start/Stop earthquake’ action part:
You can control how intense and fast the motion will be. Alternatively, you can use Lua to have even more control, as you have the option to define different forces at x,y axes respectively:
game.Quake = true -- true to start the earthquake, false to stop it game.QuakeForceX = 0 -- force at x axis game.QuakeForceY = 5 -- force at y axis game.QuakeSpeed = 5 -- speed
Get the VS object name (left column of objects in scene)
game.CurrentObject:getName() -- For object under cursor game.SavedObject:getName() -- For saved object game.UsedItem:getName() -- For the item at hand
Get the object name as it appears in game.
game.CurrentObject:getTextStr(VObjectName)) game.SavedObject:getTextStr(VObjectName))
Change the name of the object as it appears in game.
game.CurrentScene.Objects["obj"]:setTextStr(VObjectName, "string")
Note: if you want to change it in all languages you need to do a for loop through all languages.
Check if a string value is not empty (zero length)
string.len(game.CurrentObject:getTextStr(VObjectName)) ~= 0
How to clear/unlink an object value?
Using the ‘emptyObject’, e.g.:
Characters["Tom"].FollowCharacter = emptyObject
Item at hand
The currently used item, either with the ‘Set item’ action part or dragged item:
game.UsedItem
Hide an Object
Objects["bottle"].Visibility = 0
Note: it hides only the image, not the interaction polygon.
Get the Sprite position of an object
Objects["name"].Sprite.Sprite -- holds a table with information about the sprite path, position , etc
Get the position of the sprite
Objects["name"].Sprite.Sprite:getPosition().y -- for the y-axis position
Move or Check if an object has moved to another position (x,y)
game.CurrentScene.Objects[“key”].ObjectOffset
All the object areas are made of polygons which are defined by points. Practically polygons are a collection of points which are joined together. An object can have multiple polygons.
You can also access the points of an object’s polygon with Lua:
for i = 1, #Objects["big-door"].Polygon do print(i,Objects["big-door"].Polygon[i].x,Objects["big-door"].Polygon[i].y) end
You can create a new object from an existing one:
local obj = game.CurrentScene.Objects.["table"]:duplicate("duplTable") -- duplicate object is created and then stored into a variable
You can delete this new object with:
obj:remove()
The best way for having a new game functionality is to create an autosave the first time you launch a new game (e.g. just before your intro starts). Then every time you click the ‘New Button’ you can query if the autosave exists, if it does then load that; if it doesn’t then create it. Note: You have to be on a regular scene not a menu scene type for autosave to work. As a good practice, use autosave #1 only for your new game functionality.
So for example in your New Game button add the following actions:
If autosave #1 exists Load autosave #1 Else Show scene 'Intro' Εnd if
Now when your intro starts, at the beginning of scene just include at the very top:
If condition '1st-play' is true Change condition '1st-play' to false Pause for 1 second Execute autosave End if
Above we have created the ‘1st-play’ condition which is true by default because we want to create the autosave at the very first play. Note also that we added 1 second pause before executing the autosave to allow the scene to fully load first.
You could also try using the Lua replaceGame() function when you click on the ‘New Button’ but it restarts the game from the very beginning like you have just launched it.
replaceGame("data.vis")
Replace “data” with whatever you called the vis file when you export/compile the game. For testing via Visionaire Studio editor just enter your project’s ved or veb file instead.
Also note that old save files cause conflict issues when you have added new content/data to your game since it was created. While you are working on your game it is a good practice to check if an autosave exists and delete it. This will generate a fresh autosave each time you launch the game.
If autosave #1 exists Delete autosave #1 End if
Positioning of a character in a scene
To position the character above an object in a scene, you must set a YChar in the scene as follows:
YChar > YObjCenter [r], where YChar = YChar Actual + YAnim Center
So you basically need to set the YAnimCenter = YChar – YCharActual
Example: I want to place a character with YActual = 344 above an object with YAnim Center = 650, therefore I set YChar = 651, which means than i have to change YAnimCenter = 651 – 344 = 307
To be able to work with characters, the first thing to do is to retrieve their object and store it in a variable.
Get current character object
local cisco = game:getLink(VGameCurrentCharacter)
Get any character object
local kosmos = getObject("Characters[kosmos]")
Now, you can do anything with your character.
Get the position (x,y) of a character
local pos = cisco:getPoint(VCharacterPosition)
Set the position (x,y) of a character
kosmos:setValue(VCharacterPosition, {x = pos.x, y = pos.y})
Get the Direction of a character
local direction = cisco:getInt(VCharacterDirection)
Set the Direction of a character
cisco:setValue(VCharacterDirection, 0) Characters["Cisco"].Direction = 0 --shorthand game.CurrentCharacter.Direction = 0 -- for current character
Note: 0 = right, 90 = top, 180 = left, 270 = bottom
Hide/Show a character
Characters["cisco"].CharacterActive = false / true
Set the size of a character
Characters["unicorn"].Size = 50 -- in %
Disable character scaling
Characters["franco"].Size = 100 -- First set him to 100% Characters["franco"].CharacterScale = false
Disable Interaction during animation state of the current Character
You can do this from the game properties but it is possible with lua also
game.DisableInteractionDuringAnim = eDisableInteractionAlways
Possible options:
Get the animation state of a Char
You can actually listen to the animation state of a char by using the following
game.CurrentCharacter.AnimState
Where AnimState can be:
eCharacterAnim 4
eStandingAnim 3
eTalkAnim 2
eWalkAnim 1
eNoAnim 0
eRandomAnim 5
Example
if Characters["grocery-elf"].AnimState == 2 -- (or == eTalkAnim) then -- do something end
So for example, you want to check when a character is talking, possible options:
local SHOW_TEXT = 23 system.registerActionPartHook(SHOW_TEXT, "charTalk") function charTalk(actionPart) if actionPart.Link:getName() == "Tom" then -- do something end end
Prevent Character from Moving
game.LeftClickBehaviour = eMouseActionBehaviourDoNotSendCharacter -- Disable left click from updating/setting destination game.LeftClickBehaviour = eMouseActionBehaviourSendCharacterToCursor -- Enable left click update/set destination
Character talking and walking
Switch to an outfit where the character’s face/mouth isn’t drawn on. Create a secondary character that has the same canvas height & character center position as the actual character but only contains the face/mouth. Use a script to make sure that the position & alignment of the actual and the secondary characters match. Secondary character to do the talking.
Alternative Option (no lip sync is possible in this case though)
Switch to an outfit where the character also includes a talk animation with the walk animations. Set the spoken text as background (text can move with moving char)
Access current character animation
graphics.getCurrentAnimation(char)
Empty the inventory of a character
You can control what happens when a character text starts or stops by registering textStarted / textStopped event handlers:
function txtStart(text) -- this function handles actions when the char text starts local owner = text:getLink(VTextOwner) if owner:getId().tableId == eCharacters and owner:getName() == 'cisco' then -- start any actions here end end function txtEnd(text) -- this function handles actions when the char text finishes local owner = text:getLink(VTextOwner) if owner:getId().tableId == eCharacters and owner:getName() == 'cisco' then -- start any actions here end end -- * initialize text event handlers * -- registerEventHandler("textStarted", "txtStart") registerEventHandler("textStopped", "txtEnd")
Getting the text position of the character talking:
graphics.getCharacterTextPosition(Characters["dad"])
Start a dialog or a dialog layer
Use the relevant ‘Start dialog/dialog-layer’ action part.
To start a dialog from the beginning (i.e. 1st layer or super layer), choose one under ‘Characters’
You can also start a specific dialog layer/part (these are the ones that have children dialogue options) by choosing ‘Dialog Parts’.
Use a Dialog Part only once
You can have a dialog part to be selected only one time and then to be removed:
The limitation to the above is that you can only delete the dialog part being selected at a time. If you want to delete a dialog part from any other place, you need to do it with LUA, e.g.:
Characters["townhall-elf-right"].Dialogs.townhall.DialogParts[4].DialogPartAvailable = false -- this will remove the dialog part with ID 4
Access Dialog Parts
You can loop through the dialog if you want; you can access them by directly by name:
Characters.Daniel.Dialogs.feueralarm.DialogParts["1.Test"]
Generally, if you have unique texts/naming you can search them directly:
DialogParts["1.Test"]
or via table/Array number:
Characters[1].Dialogs[1].DialogParts[1]
Checking for availability:
Characters[1].Dialogs[1].DialogParts[1].Available == true
Disable a dialog option
Sometimes you want to disable a dialog option and not delete it, e.g. for testing. Just set False to an empty condition:
Switch to 1st dialog level
The dialog options allow you to switch to the previous dialog level but not the first one. To achieve this, just use the execute action and restart the dialog by adding a Start Dialog action part.
Greyed-out Dialog Options
It is possible to make dialog options that have already been chosen/used by the player to stand out, e.g. greyed-out etc. You can have different active/inactive fonts for these but adjusting the relevant option under Character’s Properties Tab:
You can also change the status of a dialog option to used or not with Lua (useful when you want to have full control over this, e.g. in nested or quit dialog options):
DialogParts["10. I'm going back to bed now."].Used = false // it will reset this back to non-used option
More Control over the Selection of Dialog Parts
With Lua you have some more control over which dialog parts can be selected which might be useful for example when you want to control this with keyboard or gamepad:
system.dialogScrollPosition = 0 -- it detects/sets the scroll position of the dialog system.dialogActiveOption = 0 -- it detects/sets the currently hovered dialog option system.dialogSelect() -- it selects the current dialogActiveOption (so make sure you set system.dialogActiveOption first)
Query the Outfit of a Character
if game.CurrentCharacter.CurrentOutfit == Outfits["sad"] then ... end if Characters["potion-man"].CurrentOutfit == Outfits["tied"] then ... end
Play a Character Animation
startAnimation(game.CurrentCharacter.CurrentOutfit.OutfitCharacterAnimations["cisco_kosmos_appear_right"]) startAnimation(Characters["cisco"].Outfits["normal"].OutfitCharacterAnimations["cisco_kosmos_appear_right"])
Set Character Speed
game.CurrentCharacter.CurrentOutfit.OutfitCharacterSpeed = 400
Random Animations
Played whenever a character is idle (i.e. doesn’t move and no animation is played) for a certain period (between 10 and 30 seconds). The animation, which is going to be played, is chosen by chance. Note: Random animations do not play also when there is text displayed on screen and a dialog is active.
You can change above times with Lua:
Characters["cisco"].CurrentOutfit.RandomMinTime = 1500 Characters["cisco"].Outfits["Surprised"].RandomMinTime = 1500 -- choose a specific outfit -- Changing the random times for all character's outfits for i = 1, #Characters["cisco"].Outfits do Characters["cisco"].Outfits[i].RandomMinTime = 1500 Characters["cisco"].Outfits[i].RandomMaxTime = 5000 end
Spine is a great tool to create 2D Skeletal animations. You can actually import your Spine animations into Visionaire and use them in your game.
You can try importing Spine’s standard example, called ‘spineboy’ into Visionaire to play around:
Using 3D character models is beneficial in many cases as it could save time from designing all character’s animation frames. Visionaire will use the 3D model to display the character in the proper angle automatically.
Note: This guide was made using Blender 2.91.0. You can also download a ready .blend model for testing from here (check video description).
Exporting from Blender
Before you export your character model, it’s suggested to rename your animations in Blender’s Outliner panel to help you assign them to the character after importing to Visionaire:
Under File -> Export -> Collada (Default) (.dae)
From the settings, you only need to adjust the axis orientation to match Visionaire orientation system, as follows:
Finally click EXPORT COLLADA button. This will create your .dae model file. If your model uses textures, the separate file for the texture will also be exported.
Importing to Visionaire
Under your character’s outfit, load the file and adjust any other settings as you wish:
Then, create a new animation under each section (walk /standing / talk etc) – you don’t have to create one for each direction, that’s the good thing with 3d models! – and then select the animation to be played (the name we set in Blender before) and adjust the speed (1 = the normal anim speed)
Now you can assign your character in the scene as you would do normally.
Rhubarb is a Lip Sync software that is supported by Visionaire. Download it from here. It generates single files from a .wav file in “tsv” format. Basically text files with start time -> phoneme.