Visionaire Studio is everything you need from a modern game engine. It’s among the best options and one of the most powerful engines to start building up your own adventure game. While it is exclusively an ‘Adventure Game’ engine, there are still many different ways to set up mechanics and experiment.
Have fun and be creative!
Visionaire is a powerful engine designed originally for 2D/2.5D point ‘n click adventure games and it’s among the best options if you plan to make such a game. It has been used in many successful commercial games, most notably by Daedalic Entertainment. You can see a comprehensive list of games made with this engine here.
This guide will help newcomers to easily setup and build their first game, but is also a handy ref for experienced developers during the production of our game. Apart from our own studio’s experience with working with the engine, this guide practically contains consolidated and structured information and examples available in the following links:
https://wiki.visionaire-tracker.net (Wiki)
http://www.visionaire-studio.com/luadocs (API Reference)
https://www.visionaire-studio.net/forum (Forum)
https://discord.com/invite/g5zFejW (Discord)
What about other game genres?
Visionaire improves its capabilities with every update. Overall, the following game types are currently possible to be made:
Main Features / Highlights
You have two options to download the engine and start playing around:
Test Version
You can download it from here. Its purpose is to test if the software fulfils your needs before buying a license. It is fully featured with the following limitations:
Full Version
You can buy a license that suits your needs and download the latest version from here. You will then receive the registration details by email and you can activate your full version under Extras -> Register Editor.
Note: if for any reason the ‘Register editor’ is not showing, edit the Viseditor.ini and delete the line showing RegisteredVersion. You can go directly to the proper folder from here:
.ved – The project file that holds all the data structure of your project in an XML format. You can also open and edit it with any text editor (only if you know what you are doing!).
.veb – AÂ compressed version of the .ved file. It holds the exact same data but it has much less size. It is not viewable or editable.
You can choose which of the above formats your project file will be in at any time when saving:
.vis – The main output file of your compiled game. It holds all your game’s assets (graphics, sounds etc.)
Below you can see the minumum required steps to have a game up and running from a blank project, considering a 3rd person game. For more details in each step you can refer to the relevant section of this guide.
See below also some more details on how to get everything up and running.
Resources
Name | Description | Author |
Creating a Scene from Scratch PDF | Tutorial on the setup of Inventory, check also the Youtube Video | W. Kruger |
Creating a Scene from Scratch Project Files | Visionaire Files for the tutorial | W. Kruger |
Visionaire uses a config.ini file to predefine the settings of the started game. The config.ini is created automatically when you build your game and is placed in the same directory as the player. The default config.ini looks like this:
FILE = data.vis # # FULLSCREEN = {yes|no} # yes - starts the game in fullscreen # no - starts the game in a window FULLSCREEN = yes # # RESOLUTION = {Auto|Desktop|Game} # Auto - wide-screen support is activated if a wide-screen display is detected # Desktop - current desktop resolution is used when game is started in full screen mode # Game - game is initialized with the resolution specified in the game RESOLUTION = desktop # # INTRO = {yes|no} # yes - Show the intro movie on start-up # no - Don't show the intro movie # # LANGUAGE = {German|English|...} # # LOGLEVEL = {Info|Warning|Error} LOGLEVEL = info
Visionaire checks the config.ini when it runs the game and adjusts the settings accordingly; if you want to change any settings edit config.ini manually before starting the game.
Dynamic Configuration
For a released game with a proper settings screen, you need to write and read from the config.ini dynamically from within the game. The example script below will allow you to do that in a new config.ini file which will be located in the localAppDir of the user. You may adjust as required to suit your game needs. For this particular one you need to create the following conditions/values on a scene:
-- * local variables * -- local fn = "config.ini" -- store filename -- * -- -- * fallback * -- local lglvl = "Error" -- default value for log level local df = "woc.vis" -- filename, should reflect exported .vis file game.SpeechLanguage = game.StandardLanguage -- default speech language to standard language -- * tables * -- local t_res = {"Auto","1280x720","1600x900","1920x1080","Desktop"} -- add available resolutions here local t_lang = game:getLinks(VGameLanguages) -- store all available languages into a table -- * function used to read data from the config.ini file * -- function read_ini() local fr = io.open(localAppDir .. fn, "r") -- read from config.ini -- * -- if fr then -- if file exists then... lines = fr:read() -- read currently selected line print("-- * --") print(fn .. " exists") print("retrieving settings from " .. fn) for lines in io.lines(localAppDir .. fn) do line = string.lower(lines) -- convert all line content to lowercase line = string.gsub(line, '\r', '') -- 5/12/2020 -> Fix for Mac / Linux. config.ini is printed with "\n" at the end of the lines, so that everything's in its own line. Windows ignores the "\n" part but mac/linux read it as "\r". So when you do if line == whatever in Windows it's if "line" == "line" for the line you're searching (Correct!) while as in mac or linux it does if "line\r" == "line" and thus fails. if not line:find("#") then -- skip all lines containing "#" if line:find("file =") then df = string.sub(lines, 8); print("file is currently linked to " .. df) end -- * window mode * -- if line == "fullscreen = no" then Conditions["cfg_fullscreen"].Value = false; print("window mode is currently set to Windowed") end if line == "fullscreen = yes" then Conditions["cfg_fullscreen"].Value = true; print("window mode is currently set to Fullscreen") end -- * resolution * -- for i = 1, #t_res do if line == ("resolution = " .. string.lower( t_res[i] )) then Values["cfg_res"].String = t_res[i]; Values["cfg_res"].Int = i; print("resolution is currently set to " .. Values["cfg_res"].String) end end -- * subtitles * -- if line == "subtitles = no" then Conditions["cfg_subs"].Value = false; print("subtitles are currently set to Off") end if line == "subtitles = yes" then Conditions["cfg_subs"].Value = true; print("subtitles are currently set to On") end -- * text speed * -- if line:find("textspeed =") then print("text speed = " .. game.TextSpeed) end -- * text language * -- for i = 1, #t_lang do if line == ("textlanguage = " .. string.lower(t_lang[i]:getName() )) then game.StandardLanguage = t_lang[i]; print("text language is currently set to " .. game.StandardLanguage:getName()); Values["cfg_language"].String = t_lang[i]:getName() end end -- * speech language * -- for i = 1, #t_lang do if line == ("speechlanguage = " .. string.lower( t_lang[i]:getName() )) then game.SpeechLanguage = t_lang[i]; print("spoken language is currently set to " .. game.SpeechLanguage:getName()) end end -- * speech timeout * -- if line == "speechtimeout = no" then Conditions["cfg_speech_timeout"].Value = false; print("speech timeout is currently set to Off") end if line == "speechtimeout = yes" then Conditions["cfg_speech_timeout"].Value = true; print("speech timeout is currently set to On") end -- * log level * -- if line == "loglevel = error" then lglvl = "Error"; print("log level is currently set to Error") end if line == "loglevel = warning" then lglvl = "Warning"; print("log level is currently set to Warning") end if line == "loglevel = info" then lglvl = "Info"; print("log level is currently set to Info") end -- * sound levels * -- if line:find("musicvolume =") then print("music volume = " .. getVolume(eMusicVolume)) end if line:find("soundvolume =") then print("sound volume = " .. getVolume(eSoundVolume)) end if line:find("speechvolume =") then print("speech volume = " .. getVolume(eSpeechVolume)) end if line:find("movievolume =") then print("movie volume = " .. getVolume(eMovieVolume)) end if line:find("globalvolume =") then print("global volume = " .. getVolume(eGlobalVolume)) end end end fr:close() print("successfully retrieved settings from " .. fn) else print(fn .. " does not exist. Setting defaults") setVolume(eMusicVolume, 70) setVolume(eSoundVolume, 75) setVolume(eSpeechVolume, 75) Values["cfg_res"].String = "Desktop" Values["cfg_res_x"].Int = 1920 Values["cfg_res_y"].Int = 1080 Conditions["cfg_subs"].Value = true Conditions["cfg_speech_timeout"].Value = true write_ini() -- creating new config.ini end end -- * function used to write data to the config.ini file * -- function write_ini() local fw = io.open(localAppDir .. fn, "w") -- write to config.ini print("-- * --") print("writing new settings to " .. fn) -- * data file * -- fw:write("File = " .. df .. "\n") -- * window mode * -- fw:write("#\n") fw:write("# Fullscreen = {Yes|No}\n") fw:write("# Yes: starts the game in fullscreen\n") fw:write("# No: starts the game in a window\n") fw:write("Fullscreen = ") if Conditions["cfg_fullscreen"].Value then fw:write("Yes\n") else fw:write("No\n") end -- * resolution * -- fw:write("#\n") fw:write("# Resolution = {Auto|Desktop|Custom}\n") fw:write("# Auto: wide-screen support is activated if a wide-screen display is detected\n") fw:write("# Desktop: current desktop resolution is used when game is started in full screen mode\n") fw:write("# Custom: enter a custom value eg: Resolution = 1920x1080\n") if Conditions["cfg_fullscreen"].Value then fw:write("Resolution = Desktop\n") else fw:write("Resolution = " .. Values["cfg_res"].String .. "\n") end -- * subtitles * -- fw:write("#\n") fw:write("# Subtitles = {Yes|No}\n") fw:write("# Yes: show subtitles during the game, cut scenes & videos\n") fw:write("# No: do not show subtitles during the game, cutscenes or videos\n") fw:write("Subtitles = ") if Conditions["cfg_subs"].Value then fw:write("Yes\n") else fw:write("No\n") end -- * text speed * -- fw:write("#\n") fw:write("# TextSpeed = the speed of the displayed text {0-100} \n") fw:write("TextSpeed = " .. game.TextSpeed .. "\n") -- * text language * -- fw:write("#\n") fw:write("# TextLanguage = {English|French|German|Spanish}\n") fw:write("# this will display subtitles in the specified language\n") fw:write("TextLanguage = " .. game.StandardLanguage:getName() .. "\n") -- * speech language * -- fw:write("#\n") fw:write("# SpeechLanguage = {English|French|German|Spanish}\n") fw:write("# this will play speech files linked to the specified language\n") fw:write("# if no speech language is provided then the speech language will default to the standard language\n") fw:write("SpeechLanguage = " .. game.SpeechLanguage:getName() .. "\n") -- * speech timeout * -- fw:write("#\n") fw:write("# SpeechTimeout = {Yes|No}\n") fw:write("# Yes: always wait for mouse click to continue the dialogue\n") fw:write("# No: do not wait for mouse click to continue the dialogue\n") fw:write("SpeechTimeout = ") if Conditions["cfg_speech_timeout"].Value then fw:write("Yes\n") else fw:write("No\n") end -- * log level * -- fw:write("#\n") fw:write("# LogLevel = {Info|Warning|Error}\n") fw:write("LogLevel = " .. lglvl .. "\n") -- * volume settings * -- fw:write("#\n") fw:write("# MusicVolume|SoundVolume|SpeechVolume|MovieVolume|GlobalVolume = int value {0-100}\n") fw:write("MusicVolume = " .. getVolume(eMusicVolume) .. "\n") fw:write("SoundVolume = " .. getVolume(eSoundVolume) .. "\n") fw:write("SpeechVolume = " .. getVolume(eSpeechVolume) .. "\n") fw:write("MovieVolume = " .. getVolume(eMovieVolume) .. "\n") fw:write("GlobalVolume = " .. getVolume(eGlobalVolume) .. "\n") print("new settings successfully written to " .. fn) fw:close() end
The main two functions of the above script are:
if Conditions["cfg_update"].Value then Conditions["cfg_update"].Value = false write_ini() end
If for any reason you need to delete the config.ini file at some point you can do so with:
os.remove(localAppDir .. "/config.ini")
Setting the game’s resolution
One of the first (and most important!) things you need to do is to set the resolution of your game through the game properties:
This will be the default resolution of your game, and normally it should match your graphics resolution.
You can get the current resolution using getProperty(“display_resolution”) which returns the rectangle that is drawn in (excluding any black borders):
local resx = getProperty("display_resolution").width local resy = getProperty("display_resolution").height
Adjusting the window size
For window modes, you may adjust the screen size on the fly with Lua:
local ScreenMultiplicator = 6; // let's say we want to make the window 6 times bigger setWindowSize({x = 320 * ScreenMultiplicator, y = 200 * ScreenMultiplicator})
Pixel Art Games
By default, Visionaire uses Linear interpolation for the graphics output which is more suitable for higher resolution games. If you are using pixel art though and want to keep the crispiness, you need to change to Nearest Neighbor interpolation by activating the pixel effect in game properties:
Using Values and Strings in Display Texts
Text Pauses
You can control how long a text will be displayed for by using <p> tags after the text as follows:
Wait until left mouse button is clicked to continue <p> Continue after 2500 milliseconds (ms) <p2500ms> Continue after 2.5 seconds (s) <p2.5s> or <p2.5> Wait until linked speech file has finished playing <pa> Wait until linked speech file has finished playing (with fallback time (in ms) if media file is missing or corrupted <pa2500ms> Automatic pause <pt> (character count * 130ms * VGameTextSpeed%)
Adjusting Text Speed
By default the display time of a text depends on the number of characters. Internally Visionaire waits for 130ms per char. So if the current text has 30 chars (including blanks, etc) it will display for 30 x 130 = 3900 ms. You can adjust this time as follows:
game.TextSpeed = 100 -- default value (in %), lowering it will slow text down.
Wait for Click to Skip Text
By implementing the textStarted event handler you have a faster way to make all your game’s text skippable only with a click and not depending on any display time:
function sText(text) if Conditions["manual_skip_text"].Value then -- create the 'manual_skip_text' condition somewhere in your game. text.TimeToWait = -1 -- this adjusts the total time (in msec) for showing current text. Setting this to -1 waits indefinitely for a click. text.WaitForAudio = false -- it ignored any linked audio file end end registerEventHandler("textStarted", "sText") -- event handler for begin text
Intercepting and Changing Text on the Fly
We can use a hook function to listen for texts at runtime and replacing them (by returning a different text):
function textFunc(obj) if obj.CurrentText == "abc" then return "def" end return obj.CurrentText end registerHookFunction("textText", "textFunc")
Query name of scene
Wrap in If lua action part:
return game.CurrentScene == Scenes["101_river"]
Check if the name of the scene contains specific string
Good for filtering scenes!
if string.match(game.CurrentScene:getName(), "minigame") then ... end
Check if a scene is a Menu
game.CurrentScene.SceneIsMenu
To add a new scene, from the top toolbar:
Choose ‘Scene’ and enter a name.
Add a background by accessing scene’s properties:
There are a couple of ways to change a scene, and they are quite straightforward using action parts.
1. Change to a new scene, relocate the character
Use the ‘Change scene’ action part. This will position the character to a specific object of another scene, align him as we need and change to this scene:
2. Change to a new scene of a specific character
We can change to a scene of any character we want using the ‘Change to scene of a character’ action part. Changing to current character can be useful also when returning to the game from a menu scene for example.
3. Show a scene or menu, without any character relocation
If we don’t need or want the character to be relocated at the time of scene change, we can just show a scene with the ‘Show scene/menu’ action part. We can relocate him manually later if we need. This action part can also show menus.
In all above action parts, you can define how the transition will be made:
Change the transition effect
For transition effects, the ‘Fade out and fade in’ effect is used by default. If we want a different effect, we can choose one using the ‘Set fade effect to new scene’ action part. We can also define the duration of the effect:
or with Lua:
game.FadeEffect = eShiftUp
There is a variety of fade effects to choose from:
Especially for the tunnel effect, you can adjust how it looks with lua:
game.FadeCenter = {x=300,y=200} -- The position of the center of the circle when you fade out the scene game.FadeInCenter = {x=600,y=700} -- The position of the center of the circle when you fade in the scene game.FadeRadius = 200 -- You can have a nice blur effect around the circle center, define the radius here
Note: the change in the fade effect will remain until you change it again.
You may also check the below tutorial for more info.
Resources
Name | Description | Author |
Scene Transitions PDF | Tutorial on the Scene Transitions, check also the Youtube Video Part | W. Kruger |
Scene Transitions Project Files | Visionaire Files for the tutorial | W. Kruger |
Way Systems tell the engine where the character is allowed to walk in the scene and how the scaling works as he moves around. To create a new way system for a scene:
You can assign a default way system to a scene through its properties.
Multiple Way Systems
You can use multiple way systems in a scene. This can useful in cases when you the way system of a system is modified, very common for example when doors open to reveal new areas. You can change the currently used way system with the relevant action part:
Each way system consists of way borders and way points, so let’s define them also.
Way borders
Way borders define the walkable area in the scene; any character cannot walk outside this area.
You define a way border by creating points; you’ll notice that your cursor will change to when you hover over the scene. Make sure you close the way border by moving the cursor over the first way point. Cursor will change to , click and you have your way border. Alternatively, you can close a way border by right clicking while you drag a way border point.