How to configure game window

Started by CopperKatana, July 16, 2019, 09:14:06 PM

Previous topic - Next topic
Hello, I'm trying to do a Solarus recreation of Golden Axe Warrior on the Sega Master System, a Zelda clone that has since been considered a hidden gem of the system.
I am trying to adjust various aspects of the display such that it closely resembles the setup of the original game. You can see my project in Solarus and a screenshot of the original game at the imgur link on the bottom.
In the original game, the map only takes up part of the screen, and the rest of the screen is colored black. I would like to replicate this if possible. What scripts or files should I edit to achieve this?

https://imgur.com/a/bGcLKX6

July 16, 2019, 09:47:17 PM #1 Last Edit: July 16, 2019, 10:07:03 PM by PhoenixII54
It is perfectly possible to achieve this HUD style, by using the camera API to resize and place it on the screen at game initialization time (generaly in the game:on_started event). For the HUD itself, you will likely want to place it in a menus/hud.lua menu script, and use the menu:on_started and menu:on_draw events to build and update the surfaces which you will display on the screen. See this page for more informations about menus, and don't hesitate to peek a view into the team's games to have an inside of their inner works.

Bonus track: you can display the amount of money(?) as "000" using string.format("%03i", your_variable) when building the text surface for the HUD

Thanks for the reply. I've been fiddling for a while but I'm having trouble understand how Solarus organizes scripts. If I want to put a camera:set_size() call in game:on_started(), what script file would I put it in? Or would I make a new script?

July 19, 2019, 12:51:31 AM #3 Last Edit: July 19, 2019, 01:04:37 AM by Kamigousu
Quote from: Blueblue3D on July 17, 2019, 02:37:09 AM
Thanks for the reply. I've been fiddling for a while but I'm having trouble understand how Solarus organizes scripts. If I want to put a camera:set_size() call in game:on_started(), what script file would I put it in? Or would I make a new script?

You could create a new script. Not sure what your directory looks like but if you have nowhere to put it all ready you could create a game.lua file either in the scripts folder or, optionally, in a subfolder you create called meta.

The script would use metatables so if you aren't familiar with them, it may be confusing. I'm not entirely familiar with them so what I have to say will be pretty basic, I think. It would also require the multi_events script that the solarus team has generously shared with their game files.

The following should change the camera size everytime the game is started; although I can't say that this will be the best or most efficient way to do this:

require("scripts/multi_events")  --This is required for this script to work. It allows the script to be called on its own.

local game_meta = sol.main.get_metatable("game")
game_meta:register_event("on_started", function()   local map = game:get_map()
                                        local camera = map:get_camera()

                                        camera:set_size(width, height)  --size will be replaced with the size of the camera on screen.
                                         --If your screen is the standard 320x240 then you could make                                           
                                         --the camera 320, 200 and it should leave a black bar at the bottom of the screen.

end)


then just require your game.lua script in either the main script (messy if you have to require too many scripts) or in another script that would then be called in the main lua.  With the sample quest, I believe there is a script called features.lua that fills this purpose. It collects many scripts into one and then is required at the beginning of the main.lua.

so at the top of your main.lua you should type this:

require("scripts/game")  --If you saved it in the scripts folder. require("scripts/meta/game") if you added the subfolder.


unless you compile many of your scripts into one feature script; which is recommended.

Hope this helps.
Build a man a fire, he will be warm for the night. Set a man on fire, he will be warm for the rest of his life.
a.k.a. Kamigousu, Xejk, Sr Xejk

I tried to implement that script, but Solarus told me it couldn't get the camera because map was a nil value. I'm not totally sure, but I think that when the game is first started, the map has not been set yet, so it's nil. Then later the map gets set and then the map gets started.

I've been studying up on the Solarus documentation a bit, and I thought to perhaps try something like the following code, where I add these camera settings to the on_started() of every map.


require("scripts/multi_events")  --This is required for this script to work. It allows the script to be called on its own.
print("Hello")  --Used for testing
local map_meta = sol.main.get_metatable("map")
map_meta:register_event("on started", function()
  camera = self.get_camera()
  camera:set_size(256, 160)
  camera:set_position_on_screen(0, 16)
  print("World")   --Used for testing
end)


This code is in a script called "camera_settings" which I have required in my main.
Unfortunately this is not working either. I'll get "Hello" but no "World," and the camera is not changed. So it would seem that it's not actually registering this function into the on_started() for my maps. Perhaps this is not a great way to do things either, but I don't quite understand what isn't working here.
I followed this code as a guide from the API for sol.main.get_metatable(type):


-- Somewhere in your main script, at initialization time:

local map_metatable = sol.main.get_metatable("map")
function map_metatable:add_overlay(image_file_name)
  -- Here, self is the map.
  self.overlay = sol.surface.create(image_file_name)
end

function map_metatable:on_draw(dst_surface)
  if self.overlay ~= nil then
    self.overlay:draw(dst_surface)
  end
end

-- Now, all your maps will have a function map:add_overlay() and an event
-- map:on_draw() that allows to draw an additional image above the map!

Ahh, yes. Map would be a nil value because it is not started at the time of the game starting. I had forgotten that and I have been writing these replies mostly from the hot kitchen I work in, so no computer access  :( . You're approach with the map meta table is on the right track although I think you have a typo that may be cause your error. I believe you should be using self:get_camera() rather than self.get_camera(). Not certain it matters, since maps are tables and the dot signifies a property of a table. Seeing as get_camera() is a method, I'm not sure it should be called that way.

Not sure why your script isn't working, but one thing you could do is define the function that you want to run on starting a new map, something like this:

local function resize_camera()
—all the code you want to use to resize the camera
end


Then  put that function as the callback in your regiseter_event line like this
map_meta:register_event("on_started", resize_camera).


That may work instead, but like I said above, I do believe you have to replace the period with a colon to make the get_camera() function work properly.
Build a man a fire, he will be warm for the night. Set a man on fire, he will be warm for the rest of his life.
a.k.a. Kamigousu, Xejk, Sr Xejk

July 19, 2019, 10:43:12 PM #6 Last Edit: July 19, 2019, 10:53:39 PM by PhoenixII54
Actully, what differenciates foo.bar() from foo:bar() is that in the second case, foo is automatically passed as an argument to the bar function, and thus it is equivalent to writing foo.bar(foo)

Anyway, your code is missing an important thing: when you called
map_meta:register_event("on_started", function()
--stuff
end)

you forgot to add an argument for the map object which is needed to fetch the camera and which is automtically passed by the event. Speaking of "self", your code will be easier to read and debug if you use an explicitely named variable for the first argument.

Quote from: PhoenixII54 on July 19, 2019, 10:43:12 PM
Actully, what differenciates foo.bar() from foo:bar() is that in the second case, foo is automatically passed as an argument to the bar function, and thus it is equivalent to writing foo.bar(foo)

Thank you, PhoenixII54, I had forgotten that. Still relatively new to lua and programming in general. In Blueblue3D's case, when they wrote

self.get_camera()

they should have instead written

self:get_camera()
or
self.get_camera(self)


, right?

Quote from: PhoenixII54 on July 19, 2019, 10:43:12 PM
Anyway, your code is missing an important thing: when you called
map_meta:register_event("on_started", function()
--stuff
end)

you forgot to add an argument for the map object which is needed to fetch the camera and which is automtically passed by the event. Speaking of "self", your code will be easier to read and debug if you use an explicitely named variable for the first argument.

To make sure I am understanding this right, you mean to say the code should look more like this?:


map_meta:register_event("on_started", function(camera)
--stuff
end)

Build a man a fire, he will be warm for the night. Set a man on fire, he will be warm for the rest of his life.
a.k.a. Kamigousu, Xejk, Sr Xejk

July 20, 2019, 01:50:43 AM #8 Last Edit: July 20, 2019, 04:41:08 AM by Kamigousu
Just finished with some testing and from what I can tell, the function that is registered with register_event() will not override the map script if the map script all ready has the function defined.

So, if your map lua has an on_started() event then the register_event("on_started", function() <-- stuff--> end) won't work.

@Blueblue3D, That may be your problem. Check the map script that you are testing this on to see if it all ready has an on_started() event defined. If so, try to comment the on_started() event out and see if your register_event(function) works.

Edit: One idea to get around this would be to put this into the game metatable and use register_event("on_map_changed", your_function). I definitely recommend defining a local function in the file you have your game_meta in. That way you can just call that function with its name(no parentheses or anything) rather than having to type everything out in the register_event() function. I tested this and it does in fact work.

I like it so much I'm actually going to use it in my game!
Build a man a fire, he will be warm for the night. Set a man on fire, he will be warm for the rest of his life.
a.k.a. Kamigousu, Xejk, Sr Xejk

July 20, 2019, 09:19:36 AM #9 Last Edit: July 20, 2019, 09:23:52 AM by PhoenixII54
QuoteTo make sure I am understanding this right, you mean to say the code should look more like this?:

map_meta:register_event("on_started", function(camera)
--stuff
end)

In occurence, it will have to be
map_meta:register_event("on_started", function(map, destination) --the destination is optional in our case, but this is for completeness regarding the API examples
  --your map initialization code
  end)


Remember that in that case, the first argument for an event callback is always the object that triggered it (here, the new map).
Quotewhen they wrote

self.get_camera()

they should have instead written

self:get_camera()
or
self.get_camera(self)


, right?

Exactly, that's the spirit.

Thank you very much for all the help! I finally got the code to work, in particular by following Kamigousu's suggestion for registering it to when the map changes.

For the reference of anyone reading this, this is in a file I called "camera_settings.lua" which I required in the main.


require("scripts/multi_events")  --This is required for this script to work. It allows the script to be called on its own.

local game_meta = sol.main.get_metatable("game")

local function set_camera(game)
  if game ~= nil then
    if game:get_map() ~= nil then
      local map = game:get_map() 
      local camera = map:get_camera()
      camera:set_size(256, 160)
      --camera:set_position_on_screen(0, 16)
    end
  end
end

game_meta:register_event("on_map_changed", set_camera)



I've also been adding HUD elements to more closely match Golden Axe, and here's a screenshot of how it's looking now:
https://imgur.com/a/PDMVz2m

Thanks again for all the help.

So, I'd like to revisit this topic because I'm now trying to do something a bit different. In Golden Axe Warrior, the HUD is on the bottom of the screen, but recently I've tried having the HUD on the top of the screen.
My problem is that when you change maps, the camera will jump up and then down again. Here's a video showing it:
https://www.youtube.com/watch?v=qXnL8aQhP0A&feature=youtu.be

I've tried many things - manipulating dst_surface in game:on_draw, for example. For all I know, it may not even be possible to get this working exactly the way I want.

July 29, 2019, 11:28:52 PM #12 Last Edit: July 29, 2019, 11:38:46 PM by froggy77
There is this message at the beginning of your video:Warning: Cannot use quest size 320x240: this quest only supports 320x320 to 320x320.
Is it normal? I read here that it should be removed. So I don't know...

on_map_changed passes the map as an argument. Use that instead of game:get_map().

Code (lua) Select
local function set_camera(game, map)
  local camera = map:get_camera()
  camera:set_size(256, 160)
  --camera:set_position_on_screen(0, 16)
end


In one of my projects I used the map:on_started() event instead of game:on_map_changed(). You could try that if the above still does not work. My code was as follows:

Code (lua) Select
--// Setup the map to be displayed in the upper-right corner of the screen (all maps)
local map_meta = sol.main.get_metatable"map"
map_meta:register_event("on_started", function(self)
local camera = self:get_camera()
camera:set_size(320, 240)
camera:set_position_on_screen(304,16)
end)

With your first script, I still get the screen jumping upward. With the second script, I find that it just results in no camera settings being changed at all. We found that if a map already had an on_started() event, then registering the event wouldn't do anything. So, I tried removing the on_started() event from the .lua files for all of the maps in my demo, and I still get the screen jumping up when it scrolls.
What version of Solarus was your project in? I'm using 1.6.0, and I wonder if maybe something was changed that has affected the behavior of the camera.