Jump to content

Animated Loading Screen with Lua


Aaron Symons
 Share

Recommended Posts

Howdy!

 

Since it looks like I can't create threads using Lua, how would I go about creating an animated loading screen during a map change using Lua?

 

The program seems to freeze during this process, and I wish to display images, text, etc and update these of their own accord while loading the next map. For example, I wish to display: an animated loading icon, and displaying an image slideshow.

 

Many thanks in advance!

Win7 64-bit | Intel i7-3770 3.40GHz | NVIDIA GeForce GTX 660

Link to comment
Share on other sites

This comes up every so often and I don't think there is an ideal situation for what you want. There is a callback in the Map:Load() where in the callback you can change images and draw to the screen but it'll still be choppy as it's loading each asset as loading takes time. You'd be better off doing images that change every so often and a progress bar that is OK to stick and not be very fluid. There doesn't seem to be a way to get total entity count before loading either so you either have to do that on your own or guess to get the max value of your progress bar. Or if you know how long it takes to generally load the map you can just make the progress go off that instead of increasing for each asset.

Link to comment
Share on other sites

Hi Rick.

 

Thanks for your help! I've got myself some "Loading..." text displaying during the map load now. Not really what I wanted, but it'll do - better than a blank screen!

 

I tried to draw the name of the current entity being loaded, but it didn't display. I might be missing something simple, so I may pursue a better loading screen in the future.

 

Thanks again for your help!

Win7 64-bit | Intel i7-3770 3.40GHz | NVIDIA GeForce GTX 660

Link to comment
Share on other sites

  • 4 weeks later...

How did you manage to even draw "loading..."?

 

My map has a good 30 seconds of loading textures and shaders before it even hits the map loading hooks.

 

Yeah, you'd want to draw it before you load the map. This also means you'd have an extra self.context:Sync(false) most likely after you draw your "Loading..." text using the context.

Link to comment
Share on other sites

The way I displayed the loading text (in App.lua):

 

function App:LoadingScreen()

self.context:SetFont(self.DefaultFont)

 

local loadingText = "L O A D I N G . . ."

local x = (App.window:GetWidth() / 2) - (self.DefaultFont:GetTextWidth(loadingText) / 2)

local y = (App.window:GetHeight() / 2) - (self.DefaultFont:GetHeight() / 2)

 

-- Fill screen with black

self.context:SetColor(0, 0, 0)

self.context:DrawRect(0, 0, App.window:GetWidth(), App.window:GetHeight())

 

-- Draw "Loading" text

self.context:SetColor(1, 1, 1)

self.context:DrawText(loadingText, x, y)

end

 

function LoadingScreen()

App:LoadingScreen()

end

 

function App:ShouldSwitchMap()

-- Clear all entities

self.world:Clear()

 

-- Load the next map

Time:Pause()

if Map:Load(self.mapfile, "LoadingScreen") == false then return false end

Time:Resume()

end

 

You probably don't need to "fill the screen with black" by drawing the rectangle, but that's what I have until I update it and I hope this helps. smile.png

  • Upvote 1

Win7 64-bit | Intel i7-3770 3.40GHz | NVIDIA GeForce GTX 660

Link to comment
Share on other sites

The way I displayed the loading text (in App.lua):

 

function App:LoadingScreen()

self.context:SetFont(self.DefaultFont)

 

local loadingText = "L O A D I N G . . ."

local x = (App.window:GetWidth() / 2) - (self.DefaultFont:GetTextWidth(loadingText) / 2)

local y = (App.window:GetHeight() / 2) - (self.DefaultFont:GetHeight() / 2)

 

-- Fill screen with black

self.context:SetColor(0, 0, 0)

self.context:DrawRect(0, 0, App.window:GetWidth(), App.window:GetHeight())

 

-- Draw "Loading" text

self.context:SetColor(1, 1, 1)

self.context:DrawText(loadingText, x, y)

end

 

function LoadingScreen()

App:LoadingScreen()

end

 

function App:ShouldSwitchMap()

-- Clear all entities

self.world:Clear()

 

-- Load the next map

Time:Pause()

if Map:Load(self.mapfile, "LoadingScreen") == false then return false end

Time:Resume()

end

 

You probably don't need to "fill the screen with black" by drawing the rectangle, but that's what I have until I update it and I hope this helps. smile.png

besides drawing a black rectangle you could use a table with the images

loadingscreen = {}
loadingscreen[0](image stuff here)
loadingscreen[1](image stuff here)
loadingscreen[2](image stuff here)
etc...

 

and just call it in the update world so as long as it's true, cycle through each pic.while it loads till it loads set to false, this would be fine showing pictures for my game, not sure if you just want pic., of course I'd rather have a animated screen, but even portal 2's loading screen some times lagg's from loading.

OS: Windows 10 Pro

CPU:  i3-10100 CPU @ 3.60GHz
GPU: NVIDIA 2060 Super - 8 GB

RAM: 32 GB

Link to comment
Share on other sites

@Aaron Does that work for you? It seems like it wouldn't because when you load the map it should call that callback function for each entity it's loading, but you are drawing to the context, but it is my understanding that whatever you draw to the context will not actually be displayed until context:Sync() is drawn (http://www.leadwerks.com/werkspace/page/documentation/_/command-reference/context/contextsync-r48). I don't see that being called in your LoadingScreen() function so how would what you are drawing there be shown on the screen?

Link to comment
Share on other sites

Wow, I can't believe I never thought of that last night. Dam.

 

I wish LE was better at map loading for a loading screen. Although there are other priorities tbh.

 

yep there are more bigger and better Priority's to be done first instead of a small thing like this(like the vegitation tool, carving tool, and road tool) biggrin.png

OS: Windows 10 Pro

CPU:  i3-10100 CPU @ 3.60GHz
GPU: NVIDIA 2060 Super - 8 GB

RAM: 32 GB

Link to comment
Share on other sites

besides drawing a black rectangle you could use a table with the images

loadingscreen = {}
loadingscreen[0](image stuff here)
loadingscreen[1](image stuff here)
loadingscreen[2](image stuff here)
etc...

 

and just call it in the update world so as long as it's true, cycle through each pic.while it loads till it loads set to false, this would be fine showing pictures for my game, not sure if you just want pic., of course I'd rather have a animated screen, but even portal 2's loading screen some times lagg's from loading.

 

Yeah. That's my next step. I'll be using Blender to render an animation to png files and cycle through them using a similar method to your suggestion. :)

 

@Aaron Does that work for you? It seems like it wouldn't because when you load the map it should call that callback function for each entity it's loading, but you are drawing to the context, but it is my understanding that whatever you draw to the context will not actually be displayed until context:Sync() is drawn (http://www.leadwerks.com/werkspace/page/documentation/_/command-reference/context/contextsync-r48). I don't see that being called in your LoadingScreen() function so how would what you are drawing there be shown on the screen?

 

This definitely works for me. There is no other code included in my Loading Screen "system", only what you see here. :)

I wonder if the call to update the context in App.lua is still running during Map:Load(), as the world (self.world) is never destroyed or created for each map load - the original always exists. ;)

Win7 64-bit | Intel i7-3770 3.40GHz | NVIDIA GeForce GTX 660

Link to comment
Share on other sites

I wonder if the call to update the context in App.lua is still running during Map:Load(), as the world (self.world) is never destroyed or created for each map load - the original always exists.

 

It shouldn't be. Map:Load() is a blocking function and doesn't come back until it's done as far as I know (yes it calls the callback for each entity it loads but shouldn't come back from the Load() call in order to reach your context:Sync() in your main loop). The world doesn't have anything to do with 2D drawing though as far as I know, but even then Sync() on the context is what draws to the screen the 2D stuff. I must be missing something here because that doesn't make sense to me how this is working. How long does your map take to load?

 

Can you post your entire App.lua file?

 

Yeah, I just tested that if you don't call Context::Sync() nothing is drawn to the screen. I just get a white screen, so I don't think this is perhaps working the way you think it is. How are you getting out of your ShouldSwitchMap() function because there is no if check in there to see if you should indeed switch maps, so if that function is being called all the time in App:Loop() then it would be switching maps all the time.

 

So my impression is whatever you are seeing was left over from the last frame that Context:Sync() was called in, but that wouldn't be what you are drawing in the map loader callback. That's why it confusing and seeing you App.lua would probably clear things up.

Link to comment
Share on other sites

I have an idea.

 

So when you load a map, it is making different Sytem::Print calls to print to the console on which texture, shader, ect it is loading.

 

In a project I did a few years ago, we had a developer who left in all his debug messages. (And there was a TON!). So to get rid of them, I overwrote the built in log function with my own. This function would still log in "development" mode, but would do nothing in production.

 

 

The following code is untested and is based on some Objective C code I wrote a year ago. Mind you this is only theoretical for C++ version of LE. Not Lua.

 

I also don't know how this will work with Visual studios and C++. I know something like this will work with GCC and C. In theory C++ gets compiled to a C library using name mangling.

 

 

static bool loadingMap = false;

void NewSystemLog(const std::string& s)

{

if(loadingMap)

{

Context* context = Context::GetCurrent();

//Draw loading screen

context->Sync(true);

}

}

 

int main()

{

void(*logFunc)(const std::string& s);

logFunc = System::Print;

 

System::Print = NewSystemLog;

}

 

// App.cpp

App::Loop()

{

loadingMap = true;

if(!Map::Load("Maps/test.map"))

{

System::Print("Error loading map");

}

loadingMap = false;

}

 

 

Since System::Print is an overloaded function what you might ahve to do is surround your code in extern "C" {} brackets and reference System::Print by the name mangled function.

 

When I get home tonight I will test it out and update with my findings.

Link to comment
Share on other sites

I have an idea.

 

So when you load a map, it is making different Sytem::Print calls to print to the console on which texture, shader, ect it is loading.

 

In a project I did a few years ago, we had a developer who left in all his debug messages. (And there was a TON!). So to get rid of them, I overwrote the built in log function with my own. This function would still log in "development" mode, but would do nothing in production.

 

 

The following code is untested and is based on some Objective C code I wrote a year ago. Mind you this is only theoretical for C++ version of LE. Not Lua.

 

I also don't know how this will work with Visual studios and C++. I know something like this will work with GCC and C. In theory C++ gets compiled to a C library using name mangling.

 

 

static bool loadingMap = false;

void NewSystemLog(const std::string& s)

{

if(loadingMap)

{

Context* context = Context::GetCurrent();

//Draw loading screen

context->Sync(true);

}

}

 

int main()

{

void(*logFunc)(const std::string& s);

logFunc = System::Print;

 

System::Print = NewSystemLog;

}

 

// App.cpp

App::Loop()

{

loadingMap = true;

if(!Map::Load("Maps/test.map"))

{

System::Print("Error loading map");

}

loadingMap = false;

}

 

 

Since System::Print is an overloaded function what you might ahve to do is surround your code in extern "C" {} brackets and reference System::Print by the name mangled function.

 

When I get home tonight I will test it out and update with my findings.

 

in lua I guess you can use a check to see if you are in debug or not to print this stuff out, in only debug and not in the product

OS: Windows 10 Pro

CPU:  i3-10100 CPU @ 3.60GHz
GPU: NVIDIA 2060 Super - 8 GB

RAM: 32 GB

Link to comment
Share on other sites

You can do an animated loading screen in Lua. But both in C++ or Lua you will have pauses as the models get loaded. Map:Load() has a callback function called for each entity. That's where you would cycle through the images and then you'd call Context:Sync() to show that to the screen.

Link to comment
Share on other sites

You can do an animated loading screen in Lua. But both in C++ or Lua you will have pauses as the models get loaded. Map:Load() has a callback function called for each entity. That's where you would cycle through the images and then you'd call Context:Sync() to show that to the screen.

 

Plus on a Side Note: this can determine if it is choppy/laggy while it is loading everything.

OS: Windows 10 Pro

CPU:  i3-10100 CPU @ 3.60GHz
GPU: NVIDIA 2060 Super - 8 GB

RAM: 32 GB

Link to comment
Share on other sites

It shouldn't be. Map:Load() is a blocking function and doesn't come back until it's done as far as I know (yes it calls the callback for each entity it loads but shouldn't come back from the Load() call in order to reach your context:Sync() in your main loop). The world doesn't have anything to do with 2D drawing though as far as I know, but even then Sync() on the context is what draws to the screen the 2D stuff. I must be missing something here because that doesn't make sense to me how this is working. How long does your map take to load?

 

Can you post your entire App.lua file?

 

Yeah, I just tested that if you don't call Context::Sync() nothing is drawn to the screen. I just get a white screen, so I don't think this is perhaps working the way you think it is. How are you getting out of your ShouldSwitchMap() function because there is no if check in there to see if you should indeed switch maps, so if that function is being called all the time in App:Loop() then it would be switching maps all the time.

 

So my impression is whatever you are seeing was left over from the last frame that Context:Sync() was called in, but that wouldn't be what you are drawing in the map loader callback. That's why it confusing and seeing you App.lua would probably clear things up.

 

Sorry! You were right. I accidentally missed out a line in my code when I posted it on here ( in LoadingScreen() ) where I Sync() the context before drawing. Sorry about that!

 

I'm not sure if syncing after drawing would make any difference, perhaps I'll experiment. I'm updating it to now show an "animated" loading icon. :)

Win7 64-bit | Intel i7-3770 3.40GHz | NVIDIA GeForce GTX 660

Link to comment
Share on other sites

Sorry! You were right. I accidentally missed out a line in my code when I posted it on here ( in LoadingScreen() ) where I Sync() the context before drawing. Sorry about that!

 

I'm not sure if syncing after drawing would make any difference, perhaps I'll experiment. I'm updating it to now show an "animated" loading icon. smile.png

 

I knew I wasn't going crazy :)

 

It matters. Nothing new is drawn if you don't sync. It'll just be the last thing that was drawn on the screen. Syncing draws everything. 2D and 3D. If you have your normal App.lua and comment out the sync call you'll get a white screen.

Link to comment
Share on other sites

I knew I wasn't going crazy smile.png

 

It matters. Nothing new is drawn if you don't sync. It'll just be the last thing that was drawn on the screen. Syncing draws everything. 2D and 3D. If you have your normal App.lua and comment out the sync call you'll get a white screen.

 

You're not going crazy at all! I think I might be regressing in IQ, though! biggrin.png

 

My last comment, I meant that I'm not sure if "Sync() then DrawText()" would make much difference to "DrawText() then Sync()" in this case, seeing as it's just a loading screen.

 

I have my animated loading screen working wonderfully now! smile.png Thank you Rick, you've been most helpful.

 

One quick aside question: is there a way to change the compression of multiple texture files at once in Leadwerks? I've just had to spend nearly twenty minutes changing the compression of over 300 texture files for my "animation" (I uncompressed them in order to have transparency, as well as nice smooth graphics). If not, hopefully I can do something through code instead, unless I'm missing something yet again. tongue.png

Win7 64-bit | Intel i7-3770 3.40GHz | NVIDIA GeForce GTX 660

Link to comment
Share on other sites

  • 2 months later...

Sorry to revive this old thread, but did anyone ever get the C++ hook to work? This is basically what I have (obviously I left out all the surrounding code, but that shouldn't have any impact on this):

 

void Loading(Entity *e, Object *o) {
 System::Print("it worked");
}
bool App::Loop() {
 Map::Load("testmap.map","Loading");
}

 

Am I calling the hook function correctly?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...