Update 5

I did some experimentation on scenery visibility this week. Scenery can improve the ratings of rides and your guests will give a scenery rating of the park. Now ideally scenery shouldn’t simply have a “sphere of influence” where everything within a certain radius of the scenery object is affected by it - for example, what if there’s something between the guest and the scenery object that is blocking the view, such as a hill or a building? In that case the scenery should have no effect.

However, doing this visibility check requires some computation time so we can’t do it every single time a guest gets close to any scenery object. A better approach would be to precalculate once from where the scenery object is visible so that we can later do quick lookups using that information to find out which objects are visible from any given place. Here’s a debug view showing the result of that precalculation:

In the center of the image is the scenery object (it’s a burger stall for testing purposes here). The white lines show from where the object is visible. Notice how there are no white lines in the bottom right corner of the image because the terrain is blocking the view, so this scenery object wouldn’t count towards the rating of any rides placed there and wouldn’t be noticed by guests walking past on that side.

The experiment showed that doing this precalculation still takes a bit too long when loading big parks but I think with some optimization it could work. It would certainly be nice to have this kind of visibility check and would allow some interesting gameplay possibilities.

On to more concrete developments!

I’ve added pricetags to things:

They don’t look nice yet but we’ll get to that when we start working on the proper UI.

Our coaster cars were sometimes not properly aligned with the track, especially in tight curves. You can kinda see it here:

And a bit better when looking at it from below:

It was barely noticeable ingame and required some bigger changes but this week it bothered me enough to finally fix it. As the fix also required some changes to the actual coaster car model I made some debug “art” for testing purposes so I could experiment with it instead of having to bother Garret every time I needed a change:

I made the cars too big but as you can see they are now properly aligned with the track and all that’s left to do is to update the real art:

Update 4

This week started with a bit more AI, in particular decision making for guests. There’s a ton of decisions they have to make (“Do I want to go on this ride?”, “How much am I willing to pay for this burger?”, “Where should I go next?”, …) so this is somewhat of an ongoing task for the entire duration of development.

AI is quite interesting to do and important to have, but it’s all under the hood - I felt the urge to also work on something that’s a bit easier to show for a change. So I made guests raise their arms on flat rides if they’re enjoying it:

Next, Garret requested a terrain leveling tool. The main challenge turned out to be finding a proper visual representation for it. We started with a simple plane that’d show how high the terrain would be after terraforming:

This turned out to be super confusing though - although you can somewhat forsee the result of the leveling it’s hard to tell which tiles are actually affected, especially when leveling somewhat higher hills.

So we tried highlighting the affected tiles instead:

This felt much better! It doesn’t tell you the final height, but you can easily see the affected tiles. The terrain gets flattened to the height at the location where you start dragging, and since you know where you clicked this is alright. The rendering of the highlighted area was quite buggy though as you can tell from the gif (notice the flickering and how the edges of the highlighted area get stretched). After using a different method for the highlights we ended up with our final version:

Lastly I started writing some calendar code for tracking the current season and how long you’ve been playing the park. We thought it’d be neat if the lighting changes depending on the time of the year - it’s just an experiment so far and will need more work, but this should give you an idea (sped up for demonstration):

Update 3

I continued working on staff AI, mainly making sure to handle situations where tasks can’t be completed.

For example, imagine there’s an employee, tasked with transporting a delivery crate containing resources from storage to a shop.
Let’s see what could possibly go wrong!

  • the employee is at the other end of the park. The task should ideally be assigned to someone who is close instead. But it can’t just be assigned to “the closest employee”, because what if they are all far away? There’s probably something else they can work on closer to where they are, it’s not very efficient to call one of them all the way over. This should only be done if no available employee has been found in close range for some time (the range can gradually expand over time).
  • the employee might not be able to reach the storage because there’s no path. Calculating paths requires relatively much time and the employee most likely won’t be able to reach the storage within the next few seconds or so either, so instead of trying to reassign the task to the same employee over and over again it should be given to another one instead without retrying (for a while).
  • eventually, some employee managed to pick up the crate and is on his way to the shop. But what if the player is doing some rebuilding and removed some paths, so the employee now can’t reach his goal anymore? He should bring the crate back to the storage. The task most likely can’t be completed for some time, so it should receive some cooldown time before trying to complete it again (performance!).
  • but what if now he can’t reach the storage anymore either? Maybe there’s another stroage he can reach and take it there? But if there’s no reachable stroage? There’s really nothing he can do, so he should probably just drop the crate, carry on with some other task and have someone else pick it up later. Here’s where the Behaviour Trees from last weeks post come in handy, as they allow to quite easily define a range of different strategies.
  • what if the shop doesn’t exist anymore? The task for delivering the resources has to be cancelled and they wait in storage until they are needed elsewhere.

So that’s mostly what I did this week - ensuring we have systems in place to handle cases like these.

Update 2

I continued to mainly work on behind-the-scenes stuff.

I made sure path supports work properly on sloped terrain; the fundaments wouldn’t appear before:

Next I made sure that when drag-building paths already occupied spots are highlighted red and aren’t build (and you don’t pay for them again):

Garret made some new cursors for the terraform tool, and I extended the tool by the ability to raise multiple tiles at once or just individual corners. It still needs more work and requires additional features, for example the ability to flatten a whole area of land to the same height, but for now this is pretty usable.

I made trees subtly sway in the wind (so subtle it’s barely visible in the gif except for the cut):

Then I started implementing shopkeepers, but so far they don’t do more than walk to their shop.

The rest of the week was spent on researching and implementing Behaviour Trees - I don’t want to get too technical here (see the link for a pretty nice explanation), so the short summary is that this is a method for structuring AI code into many smaller reusable parts that can then be plugged together to tell an agent what to do. For example, the code that tells the shopkeeper to walk to his shop looks like this:

new Sequence( // do the following actions one after another
	new FindPathAction(), // find a path to the destination
	new FollowPathAction(), // walk to the destination
	new RotateToQuaternionAction() // turn around to look outside window
);

I can’t imagine more comfortable code than this, and so far it looks like it’ll be a great tool for creating our AI behaviours :)