How To Code a Falling Sand Simulation (like Noita) with Cellular Automata

MARF
23 May 202121:18

Summary

TLDRThis video script delves into the revival of falling sand simulations, leveraging modern computing power to enhance the cellular automata concept. It outlines the process of coding a simulation with a 2D grid, detailing the behavior of elements like sand, stone, and water. The script explores coding structures, optimizations for performance, and introduces unique elements and behaviors, such as particles and explosions. The speaker shares their journey and invites feedback, providing a comprehensive look at creating dynamic and organic simulations.

Takeaways

  • 🕰️ Falling sand simulations, once limited by computational power, have been revitalized with modern hardware capabilities.
  • 🌐 The concept is based on cellular automata, sharing similarities with games like Conway's Game of Life, Minecraft, and Noita.
  • 🔲 In a falling sand simulation, the world is a 2D grid where discrete elements follow predefined rules based on their surroundings.
  • 🧱 Elements like stone, sand, and water have unique behaviors; stone is static, while sand and water move based on available space.
  • 📚 The simulation uses a 2D array to store the matrix and a wrapper class to manage interactions and avoid complex issues.
  • 📐 Elements are structured in a class hierarchy allowing for shared and overridden methods for flexible behavior customization.
  • 💧 Water can spread out faster by looking further for space, and a variable dispersal rate can be set at the class level for different liquids.
  • 🔢 An algorithm uses the slope of a line to calculate movement between any two cells, allowing for consistent pathfinding.
  • 🔄 When sand falls from a height, it can now spread out more naturally by conserving energy through horizontal movement and friction.
  • 🔍 The 'is free falling' flag simulates inertia, changing the movement behavior of elements based on whether they are moving or at rest.
  • 💥 Particles, a new element type, can simulate effects like explosions, using the same traversal function but with different interaction logic.

Q & A

  • What is a falling sand simulation?

    -A falling sand simulation is a type of computational model that represents a world as a 2D grid where each cell can contain one element at a time. Elements follow predefined rules based on their intrinsic motivations and surrounding environment, acting independently of each other.

  • How is a falling sand simulation related to cellular automata?

    -A falling sand simulation is a derivative of cellular automata, which are computational models where space and time are discrete and elements interact with their local environment following certain rules.

  • What are some examples of games that use concepts similar to falling sand simulations?

    -Examples of games that borrow concepts from falling sand simulations include Conway's Game of Life, which is a true cellular automata, Minecraft with its blocky appearance and behavior, and Noita, a popular falling sand roguelike game.

  • How does the movement of sand elements work in a falling sand simulation?

    -In a falling sand simulation, sand elements first attempt to move downward one cell. If they encounter an obstacle, they try to move diagonally downward left or right until they find an empty cell or are unable to move further, in which case they stay in place.

  • What is a matrix wrapper class in the context of a falling sand simulation?

    -A matrix wrapper class is a class that encapsulates the 2D matrix used in a falling sand simulation. It handles setup and mediates interactions with the inner 2D array, providing a controlled way to access and modify the simulation's state.

  • Why is it beneficial to structure element classes in a hierarchy for a falling sand simulation?

    -Structuring element classes in a hierarchy allows for common method definitions to be placed in the highest applicable abstraction, reducing code duplication. It also enables easy customization of behavior in subclasses by overriding methods, making it simpler to create new unique elements with specific interactions and parameters.

  • How does the movement of water elements differ from sand in the simulation?

    -Water elements have similar movement logic to sand, but if they cannot find space below, they search horizontally to spread out. Water can also look further for an available space when encountering another element, which can be adjusted by a variable stored at the class level to control different dispersal rates.

  • What is the purpose of the 'is free falling' flag in movable solid elements?

    -The 'is free falling' flag indicates whether a movable solid is currently moving or not. If set to true, the element will look for places to move diagonally downward when it hits an obstacle. If set to false, the element will only look directly downward, making it more likely to stay in place once it has come to rest.

  • How can the simulation handle elements with non-zero velocities on both the x and y axes?

    -The simulation can handle elements with non-zero velocities on both axes by using a matrix traversal algorithm that calculates the shortest path between any two given cells. This allows elements to move in a straight line from their current position to a target position, checking every cell along the way.

  • What are some performance optimizations discussed in the script for a falling sand simulation?

    -Some performance optimizations discussed include multi-threading to process multiple cells concurrently and chunk-based processing where chunks of the world are marked for processing based on the activity of their contained elements, reducing unnecessary calculations.

  • How does the script suggest handling explosions in a falling sand simulation?

    -The script suggests creating a particle element type for handling explosions. When an explosion occurs, elements within a certain radius are affected based on their explosion resistance. The explosion propagates from the center outward, and the simulation uses a matrix traversal method to determine the path and effect of the explosion.

Outlines

00:00

🌟 Reviving Falling Sand Simulations with Modern Coding Techniques

This paragraph introduces the concept of falling sand simulations, which have been revitalized due to the increased computational power of modern computers. The speaker explains how these simulations, which are a form of cellular automata, can be enhanced with clever coding and imagination. Examples of games that use similar concepts include Conway's Game of Life, Minecraft, and Noita. The paragraph also describes the basic mechanics of a falling sand simulation, where elements like sand, stone, and water interact based on predefined rules within a 2D grid. The speaker outlines the independent behavior of each element and how they respond to their surroundings, as well as the process of coding these simulations using a 2D array and a class hierarchy to manage different types of elements.

05:00

💧 Enhancing Liquid Dynamics and Element Interactions

The second paragraph delves into the specifics of improving the behavior of liquids in the simulation, particularly water. The speaker discusses the limitations of basic movement and introduces a method to allow water to disperse faster by looking further for available spaces. This is achieved by modifying the water's behavior to search horizontally when no space is found below. The paragraph also covers the implementation of a matrix traversal algorithm that enables elements to move between any two points in a 2D array in a logical manner, which is applied to user drawing brushes and particle movement. Additionally, the speaker explores making the simulation more organic by introducing energy conservation in falling sand and simulating inertia through an 'is free falling' flag, which affects how elements like sand move after coming to rest.

10:01

🔥 Introducing Particles and Explosion Dynamics

In this paragraph, the speaker introduces a new element type called 'particle' for simulating phenomena like explosions or water fountains. Particles inherit from the abstract element class and utilize the same matrix traversal function for movement as other elements, but with a simple interaction logic where they replace themselves with their contained element upon collision. The speaker also discusses creating realistic explosions that propagate from the center outward, stopping when encountering an element with sufficient explosion resistance. This is achieved by using a matrix traversal method to check each cell's explosion resistance and either destroy the element or break early from iterating. The paragraph concludes with the idea of creating a variety of new element types with minimal effort by adjusting parameters and specifying interactions in the 'act on other' method.

15:18

⚙️ Optimizing Performance for Large-Scale Simulations

The fourth paragraph focuses on performance optimizations for handling large-scale simulations efficiently. The speaker describes a significant drop in frame rate when operating on a large number of cells and introduces multi-threading as a solution. By splitting the world into columns and processing them in separate threads, the simulation's performance is greatly improved. The speaker also discusses the issue of elements repeatedly attempting to move into occupied cells, which leads to unnecessary calculations. To address this, the world is divided into chunks, each with a state indicating whether it should be processed in the current or next frame. This approach significantly reduces unnecessary processing and, when combined with multi-threading, brings the frame rate back up to an acceptable level.

20:19

🔄 Continuous Development and Future Improvements

In the final paragraph, the speaker reflects on the ongoing development of the simulation over the past two years, highlighting the challenges faced and the solutions implemented. The speaker acknowledges that they are not a professional game developer and that the methods discussed may not be the best but have worked for them. They express openness to suggestions for improvement and invite questions from viewers. The speaker also mentions plans to port the simulation to C++ and SFML for better drawing performance and hints at potential future videos discussing these developments. The paragraph concludes with a review of the game loop, detailing the steps taken each frame and the setup logic executed on startup.

Mindmap

Keywords

💡Falling Sand Simulation

Falling Sand Simulation refers to a computational model that simulates the behavior of granular materials like sand. It is a derivative of cellular automata and is used in the video to demonstrate how elements like sand, stone, and water interact in a virtual 2D grid. The concept has been revisited due to increased computing power, allowing for more complex and realistic simulations, as illustrated by the video's exploration of different elements' behaviors and interactions.

💡Cellular Automata

Cellular Automata are mathematical models used to simulate dynamic systems based on discrete cells, each with a set of states and rules governing their transitions. In the context of the video, a Falling Sand Simulation is a type of cellular automata where each cell can contain one element, and the state of each cell evolves based on its own rules and the states of neighboring cells, as seen in the basic example provided with sand, stone, and water.

💡2D Matrix

A 2D Matrix, as mentioned in the script, is a two-dimensional array used to represent the grid of the simulation. Each element within the matrix corresponds to a cell in the grid, which can contain different elements like sand, stone, or water. The video explains how the simulation iterates through this matrix to update the positions and states of the elements, which is central to the functioning of the Falling Sand Simulation.

💡Element Behavior

Element Behavior in the video refers to the predefined rules that dictate how each type of element in the simulation reacts to its environment and intrinsic motivations. For instance, the script describes how sand moves downward or diagonally if it encounters obstacles, while water spreads horizontally if it cannot move downward. These behaviors are crucial for creating realistic interactions within the simulation.

💡Inheritance (in programming)

Inheritance is a fundamental concept in object-oriented programming where a class can inherit properties and methods from another class. The video uses inheritance to create a hierarchy of element classes, with an abstract base class and subclasses for different types of elements like liquids, gases, and solids. This allows for shared behavior across multiple element types while also enabling specific customization through overridden methods.

💡Matrix Traversal Algorithm

The Matrix Traversal Algorithm discussed in the video is a method for determining the shortest path between two cells in the 2D matrix. It is used for various purposes, such as moving elements diagonally or simulating the spread of water. The algorithm calculates the slope of a line between points and iterates through the x-values to find the corresponding y-values, ensuring elements move in a logical and consistent manner.

💡Optimization

Optimization in the context of the video pertains to improving the performance and efficiency of the simulation. The script describes two main optimization techniques: multi-threading to process multiple cells concurrently and chunk-based processing to avoid unnecessary calculations on resting elements. These optimizations are crucial for handling the large number of cells in the simulation without compromising the frame rate.

💡Multi-threading

Multi-threading is a technique used in the video to enhance the simulation's performance by allowing different parts of the 2D matrix to be processed simultaneously. By dividing the matrix into columns and processing odd and even columns in separate threads, the video demonstrates a significant improvement in the simulation's frame rate, highlighting the effectiveness of this approach in handling large-scale computations.

💡Chunk-based Processing

Chunk-based Processing is another optimization technique mentioned in the script, where the 2D matrix is divided into chunks, and each chunk decides whether to process its contents based on the activity of its elements. This method reduces the computational load by avoiding processing on chunks that are 'sleeping' or not changing, thus conserving resources and improving the simulation's performance.

💡Particle Element

The Particle Element is a special type of element introduced in the video for simulating phenomena like explosions or water fountaining. It inherits directly from the abstract element class and uses the matrix traversal function for movement without any interaction logic. When a particle encounters another element, it replaces itself with its contained element, allowing for versatile and dynamic visual effects within the simulation.

💡Inertial Resistance

Inertial Resistance is a variable used in the video to simulate the tendency of an element to remain at rest or to continue moving. It is applied to movable solids to determine how likely they are to start moving again after coming to a rest. By adjusting the inertial resistance value, the video shows how elements like sand, dirt, and coal can have different behaviors in how they crumble and settle, contributing to a more organic appearance of the simulation.

Highlights

Falling sand simulations, once dormant, are now revisited with modern computing power.

Conceptual coding of falling sand simulations explained through illustrations and code samples.

Falling sand sim as a derivative of cellular automata, with examples like Conway's Game of Life and Minecraft.

World represented by a 2D grid where elements have discrete behaviors based on surroundings.

Elements like sand, stone, and water have predefined behaviors influenced by their environment.

Sand forms pyramid-like structures when piled up, and water spreads horizontally when space is limited.

Sand can sink when encountering water, demonstrating element interaction in the simulation.

2D matrix stored as an array, with a wrapper class to handle setup and interactions.

Element classes structured in a hierarchy for efficient code and customizable behavior.

Pseudo code for movable solid class illustrates element processing during simulation.

Water dispersal improved by looking further for space, introducing variable dispersal rates.

Gravity and velocity introduced to simulate faster movement and multiple cell traversal.

General purpose algorithm for shortest path traversal between any two cells in a 2D array.

Drawing brush elements use matrix traversal for smooth and continuous drawing.

Sand's behavior modified to conserve energy upon impact, simulating a more realistic spread.

Inertial resistance variable introduced to simulate the resting state of elements.

Particle element type created for effects like explosions, utilizing matrix traversal for movement.

Optimizations like multi-threading and chunk-based processing to improve simulation performance.

Custom shaders and texture updates explored for more efficient drawing of elements.

Flowchart provided for the game loop, detailing the simulation's frame-by-frame operations.

Transcripts

play00:00

falling sand simulations are a

play00:02

decades-old concept which have been

play00:03

mostly dormant since the early 2000s era

play00:06

of standalone java applets

play00:08

now that the average computer is much

play00:09

more powerful than two decades ago

play00:11

this concept has been revisited and

play00:13

falling sand sims can be pushed further

play00:14

with some clever coding and imagination

play00:17

in this video i will explain

play00:18

conceptually how to code a falling sand

play00:20

simulation

play00:21

showing through illustration and code

play00:22

samples how i solved different technical

play00:24

challenges

play00:26

first let's cover what a falling sand

play00:27

simulation is

play00:29

a falling sand sim is a derivative of

play00:31

cellular automata

play00:32

some examples of other games which

play00:34

borrow concepts from this are conway's

play00:36

game of life which is a true cellular

play00:38

automata

play00:39

minecraft pulls related elements with

play00:40

its blocky appearance and behavior

play00:43

and noita is a recent popular falling

play00:44

sand roguelike

play00:46

in a falling sand simulation the world

play00:48

is represented by a 2d grid

play00:50

each coordinate in the matrix is

play00:51

discrete meaning that only one element

play00:53

can occupy any cell

play00:54

at one time elements cannot be partially

play00:57

in a cell

play00:58

and cannot be in two cells at once each

play01:00

element has predefined a behavior or

play01:02

rules which allow it to act based on its

play01:04

intrinsic motivations and also its

play01:06

surrounding environment

play01:08

each element acts independently and

play01:09

while its surroundings can influence the

play01:11

way it acts

play01:12

the behavior is executed independently

play01:13

for each element

play01:15

let's take a basic example with the

play01:17

elements sand stone and water

play01:19

our simulated world is represented by a

play01:21

2d matrix

play01:23

in each frame we iterate through all the

play01:25

elements in the matrix from the bottom

play01:26

row to the top

play01:28

empty cells require no processing so we

play01:30

will skip over these

play01:31

the stone is static and does not move

play01:33

regardless of its surroundings or forces

play01:35

so no further update is needed

play01:37

the sand elements rules determine that

play01:39

it will always first attempt to move

play01:41

downward one cell

play01:42

if it encounters stone or other sand it

play01:44

will attempt to go diagonally downward

play01:46

left or right until it is unable to find

play01:48

any empty cells

play01:49

in which case it will stay in the same

play01:50

location if you add a bunch of sand into

play01:53

the simulation on top of each other it

play01:54

will form pyramid-like structures

play01:57

water has similar movement logic to sand

play01:59

except if it cannot find any space below

play02:00

it then it will search for space

play02:02

horizontally

play02:03

allowing it to spread out further we can

play02:05

expand the sand behavior so that if

play02:06

encounters water then they will switch

play02:08

places effectively allowing the sand to

play02:10

sink

play02:13

now that we have discussed a basic

play02:14

scenario i'll explain how to structure

play02:16

this in code

play02:17

the 2d matrix is stored as a 2d array or

play02:20

an array of arrays

play02:22

i have created a wrapper class around

play02:23

this matrix which handles setup and

play02:25

mediates any interactions with the inner

play02:27

2d array

play02:28

since the matrix is modified in place

play02:30

each frame by limiting the avenues

play02:32

through which the inner array can be

play02:33

accessed we can avoid

play02:35

unexpected or harder to trace issues as

play02:37

the complexity of the simulation

play02:39

increases

play02:40

for example standardizing the way

play02:41

elements swap sales reduces repeated

play02:43

code

play02:44

and makes debugging easier i structured

play02:46

my element classes in the following

play02:48

hierarchy

play02:49

there is a base abstract element class

play02:51

which has subclasses for liquid

play02:53

gas and solids which then has further

play02:55

subclasses for movable solids

play02:57

such as sand and immovable solids such

play02:59

as stone

play03:00

this structure allows you to place

play03:02

method definitions common to

play03:04

all types of elements in the highest

play03:05

applicable abstraction

play03:07

and this also allows you to override

play03:08

these methods in subclasses for

play03:10

customizable behavior

play03:12

for example i have a base method in the

play03:14

abstract element class which defines

play03:16

what elements should do when they

play03:17

receive heat from an ignited neighboring

play03:19

element

play03:20

this common logic applies to many

play03:22

elements but if i want water to turn

play03:24

into steam when receiving heat

play03:26

i can make that custom logic easily by

play03:28

overriding the method in the water class

play03:30

if i want stone to have no effect when

play03:32

receiving heat i can override the method

play03:33

with a blank definition

play03:35

if i don't want any liquids to have

play03:37

their color affected by explosions

play03:39

i can override that method at the liquid

play03:40

class level

play03:42

once you have the common logic set up

play03:44

for each of the main element types it

play03:45

becomes easy to create new unique

play03:47

elements by only tweaking parameters and

play03:49

specifying element interactions

play03:52

let's walk through some pseudo code for

play03:54

the step function on the movable solid

play03:55

class

play03:56

to get a better understanding of what

play03:57

happens during the processing stage of

play03:59

an individual

play04:00

element in this case sand first we get

play04:03

the contents of the cell below the

play04:04

current object cell

play04:06

if the target cell is empty we can

play04:08

simply move our piece of sand

play04:09

into the empty cell if the target cell

play04:12

contains any class inheriting from the

play04:13

liquid abstract class

play04:15

we will switch places with it and if the

play04:17

target cell contains any type of solid

play04:19

movable or immovable we will look

play04:21

diagonally for a new occupiable cell

play04:24

let's evaluate what we have so far it is

play04:26

predictable and consistent

play04:28

but how can we improve it water takes so

play04:30

long to spread out

play04:31

since it can only move one cell at a

play04:32

time in order to make the water disperse

play04:35

faster

play04:35

we can make it look further for an

play04:37

available space when encountering

play04:38

another element

play04:39

so if water looks below itself for an

play04:41

empty space and finds none then it will

play04:43

look x amount of cells to the right or

play04:45

left for an empty space

play04:47

let's make this a variable and store it

play04:49

at class level so different liquids can

play04:50

have different dispersal rates

play04:52

if the water simply looks five places to

play04:54

the left or right and occupies the empty

play04:56

space it finds there then it will

play04:58

effectively teleport and bypass any

play05:00

elements in between

play05:02

when the liquids travel multiple cells

play05:03

to any side it must check every cell

play05:05

between its current location and its

play05:07

destination

play05:08

otherwise it would pass through walls a

play05:10

simple solution is to use a basic for

play05:12

loop to move horizontally

play05:14

stopping when we hit another element and

play05:16

this works

play05:17

we can compare the old behavior with a

play05:19

new behavior to see that liquids are now

play05:20

dispersed much faster than previously

play05:28

we can apply this same concept

play05:30

vertically by adding gravity to the

play05:32

simulation

play05:32

and tracking an element's velocity with

play05:34

a 2d vector

play05:35

this allows it to increase in speed and

play05:37

travel more than one cell per frame

play05:39

again we want to check every cell along

play05:41

the way but what if we wanted an element

play05:44

to have both non-zero

play05:45

x and y axis velocity we can't just do a

play05:48

simple for loop or even a nested for

play05:50

loop

play05:51

this raises the question how can we

play05:53

travel between any two given cells in

play05:55

our 2d array

play05:56

following the shortest path we can

play05:58

create a general purpose algorithm for

play06:00

this

play06:00

it is generic enough that i use it for

play06:02

the user's drawing brush

play06:04

elements traveling through the 2d array

play06:06

particle movement and for the center

play06:08

explosion method i'll cover later

play06:11

fortunately there already is an equation

play06:13

to find the slope of a line drawn

play06:14

between two points

play06:16

the slope allows us to calculate the

play06:17

corresponding y values as

play06:19

x increases we can use a for loop to

play06:21

iterate to the desired x value since in

play06:23

this case

play06:24

x is the longer side each cell is

play06:26

discrete so if the calculated y value is

play06:28

not a whole number we will round up or

play06:30

down based on the first decimal place

play06:32

we can step through this example

play06:34

calculating the rounded y value for each

play06:36

x value

play06:36

and filling in the corresponding cell

play06:48

can see that the path taken closely

play06:50

follows the line drawn between the start

play06:52

and end point

play06:53

the actual algorithm becomes a little

play06:55

more convoluted when taking into account

play06:57

all possible directions the path can be

play06:59

so i have created an annotated gist

play07:01

which is linked in the description

play07:03

using this approach we can travel

play07:05

between any two points regardless of

play07:07

where they are

play07:07

in a logical and consistent manner a

play07:10

practical application of this method is

play07:12

used with the drawing brush

play07:14

previously an element would only be

play07:16

drawn at the mouse location each frame

play07:18

but since the mouse location can move a

play07:20

large distance between frames

play07:22

this would create a disjointed series of

play07:24

elements using a large brush size and

play07:26

moving slowly would create connected

play07:28

elements

play07:29

but with this matrix traversal algorithm

play07:31

elements are drawn between drastically

play07:33

different mouse locations frame to frame

play07:35

in an expected and believable manner

play07:37

now that we have sand stone and water

play07:39

working together let's explore some

play07:41

changes to make the behavior slightly

play07:42

less predictable but a little more

play07:44

organic in appearance

play07:47

when a grain of sand falls from a great

play07:49

height it will just smack the ground and

play07:50

stop moving

play07:51

because these are the rules that we gave

play07:52

it but it would make more sense for some

play07:54

of the energy to be conserved and push

play07:56

the sand

play07:57

elsewhere what we can do is when a grain

play07:59

of sand hits an obstacle at a high

play08:01

velocity

play08:02

we take a proportion of the vertical

play08:03

velocity and convert it to horizontal

play08:05

velocity

play08:06

so now when the sand hits the ground it

play08:08

will move sideways conserving some of

play08:10

that energy

play08:11

but now the sand endlessly runs sideways

play08:14

by applying some friction to reduce its

play08:16

horizontal velocity each time it comes

play08:18

in contact with an obstacle

play08:19

we can mitigate this behavior and the

play08:21

sand comes to a stop

play08:23

this allows sand to spread out a bit

play08:25

further when falling from a height

play08:26

and pile as it forms are no longer

play08:28

perfect pyramids

play08:32

another rule we can expand upon for the

play08:34

movable solids is where it looks to go

play08:36

each frame

play08:37

we can simulate one aspect of inertia by

play08:39

adding a flag to each movable solid

play08:41

indicating if it is currently moving or

play08:43

not i chose the name is free falling

play08:46

if the element did not change cells

play08:48

between this frame and last frame then

play08:49

is free falling is set to false

play08:51

otherwise it will be set to true while

play08:54

israel falling is set to true

play08:55

our sand will perform the previously

play08:57

mentioned collision behavior

play08:59

of checking all three cells below it for

play09:00

space

play09:02

when this flag is set to false then the

play09:04

sand will only look directly downward

play09:06

each frame for a place to go and no

play09:08

longer diagonally

play09:09

so once a solid comes to a rest it is

play09:11

more likely to stay there

play09:13

the way a solid can have is free falling

play09:15

set to true again

play09:16

and thereby look for places to go

play09:18

diagonally downward

play09:19

is either by the space below it no

play09:21

longer being occupied and it falls

play09:23

downward

play09:24

or when an adjacent element passes by

play09:26

and attempts to dislodge it

play09:28

elements with israeli falling set to

play09:30

true will check adjacent neighbors and

play09:32

attempt to set there is free falling

play09:34

flag to true

play09:36

you can see here that once the elements

play09:37

come to a rest i can delete the elements

play09:40

adjacent to them

play09:41

opening up a diagonal pathway for the

play09:42

elements on the side of the stack to

play09:44

move into

play09:45

but they do not take it since they have

play09:47

is free falling set to false

play09:49

but by dropping some new elements past

play09:51

these stationary piles

play09:52

we can trigger is free falling to be set

play09:54

to true causing one element to move

play09:56

again which causes a chain reaction for

play09:58

the whole stack

play09:59

the chance of history falling being set

play10:01

to true is based on the inertial

play10:03

resistance

play10:04

variable we assigned to it by adjusting

play10:06

the inertial resistance value we can

play10:08

change how likely an element is to be

play10:10

set back to free falling state

play10:12

we can compare the results side by side

play10:14

with the elements sand

play10:15

dirt and coal each with different

play10:18

inertial resistance values

play10:19

the higher the value the harder it is

play10:21

for an element to have is free falling

play10:23

set to true again these two features

play10:25

combine to make more organic looking

play10:27

stacks of elements and improve the way

play10:29

they look as they crumble

play10:31

we can see the old behavior compared to

play10:32

the new here

play10:40

so far all of our logic for elements has

play10:42

been with the assumption that everything

play10:44

moves downwards

play10:45

but what if we want to have sand flying

play10:47

around from explosions or water

play10:49

fountaining up from the ground

play10:50

we can create a new element type called

play10:52

particle this element will inherit from

play10:55

the abstract element class directly

play10:57

and we can utilize the same matrix

play10:59

traversal function for movement that

play11:01

other elements use

play11:02

but the particle element will have no

play11:04

interaction logic

play11:05

if a particle hits any other type of

play11:07

element it will replace itself with its

play11:09

contained element

play11:10

so let's say a force is applied to a

play11:12

piece of sand which would launch it into

play11:14

the air

play11:15

we remove the element from the matrix

play11:17

and replace it with a particle of the

play11:18

same color

play11:19

a reference to the element we just

play11:21

removed and apply the force to the

play11:23

particle instead

play11:24

if the particle encounters another

play11:26

element it will remove itself from the

play11:27

matrix

play11:28

and place the sand element in its

play11:29

current cell this keeps particle logic

play11:32

simple and versatile particles can also

play11:35

have no containing element and simply

play11:37

die when they hit something for a visual

play11:39

only effect

play11:41

explosions could be as simple as

play11:43

checking if an element is within a blast

play11:45

radius and destroying it if the

play11:46

explosion is strong enough

play11:48

but that would lead to more vulnerable

play11:50

elements being destroyed behind tougher

play11:51

elements which isn't predictable

play11:53

behavior

play11:54

i really like the way noita has

play11:56

explosions move from the center outward

play11:58

and don't affect weaker elements behind

play12:00

tougher elements

play12:01

so let's create an explosion which

play12:03

propagates from the center outward

play12:05

and stops once it reaches an element

play12:06

which has an explosion resistance higher

play12:08

than the strength of the explosion

play12:11

first we take a radius which represents

play12:13

the size of the explosion

play12:14

and make a box around the center of an

play12:16

origin point

play12:17

for each point on the perimeter of the

play12:19

square we utilize our matrix traversal

play12:21

method

play12:22

and travel from the origin point to the

play12:24

point on the perimeter

play12:26

as we travel across each cell we check

play12:28

if distance from the center is less than

play12:30

our determined radius so our area of

play12:32

effect is a circle and not a square

play12:35

and check the explosion resistance of

play12:36

the element present in the cell

play12:38

we either destroy it or break early from

play12:40

iterating to the square's perimeter

play12:43

there will be a lot of overlap in the

play12:44

paths from the origin point to the edge

play12:46

points

play12:46

on the box but we do not want to operate

play12:49

on the same cells twice

play12:51

we can cache the result so when we visit

play12:53

the same cell again

play12:54

we don't need to duplicate our actions

play12:56

the cached value for each cell indicates

play12:58

that the explosion was blocked at this

play13:00

location previously

play13:01

and should stop here again or if the

play13:03

explosion can continue along this path

play13:05

and act on further cells

play13:08

to achieve this targeting effect instead

play13:10

of just stopping when encountering a

play13:12

tough material

play13:13

a flag is set in the cache which

play13:14

indicates the explosion is no longer

play13:16

destroying elements past this point

play13:18

but just calling a darkened method

play13:20

common to elements

play13:22

add a random chance of falloff and you

play13:23

get some scorch marks which give a nice

play13:25

effect

play13:28

now that we have created some adjustable

play13:30

parameters for our solid and liquid

play13:32

classes

play13:33

we can work on creating a variety of new

play13:35

element types with minimal effort

play13:37

first let's revisit our step function

play13:39

pseudocode

play13:40

when an element collides with another

play13:42

element the act on other method is

play13:44

called on the current cell

play13:46

this act on other method is blank at the

play13:48

abstract element class

play13:49

level which is good for elements like

play13:51

sand and dirt which don't have any

play13:53

special effect on other element types

play13:55

but makes it easy to add custom logic

play13:57

for water to apply cooling

play13:58

or lava to apply melting

play14:01

try to make the acid element type which

play14:03

does damage to any elements it touches

play14:06

and dies once it has touched enough

play14:07

other elements and see how easy it is to

play14:09

do so

play14:10

we need to have the acid class inherit

play14:12

from the liquid class

play14:14

and then set some liquid specific

play14:16

parameters such as density and

play14:17

dispersion rate

play14:19

the method which acid will use to act on

play14:21

other elements will be called corrode

play14:23

we make a default definition in the

play14:25

abstract element class

play14:27

which means all element types will be

play14:28

affected by the corrode method

play14:31

we can create empty definitions of

play14:32

corrode in the acid class

play14:34

so that it doesn't corrode other acid

play14:36

and we can make another immovable solid

play14:38

called titanium which we don't want to

play14:40

have affected by corrode either

play14:43

that's all we need to do and now we have

play14:44

a fully functioning new element type

play14:49

now i'll show some other unique elements

play14:51

with their effects and the corresponding

play14:53

code required to make them work

play15:01

[Music]

play15:17

[Music]

play15:25

you

play15:32

[Music]

play15:50

operating on hundreds of thousands of

play15:52

cells in our 2d matrix at

play15:54

60 frames per second is a costly

play15:56

procedure and the features we just added

play15:58

only slow it down further

play16:00

there are some optimizations we can

play16:01

implement which will drastically improve

play16:03

performance of our simulation

play16:05

let's get a baseline before implementing

play16:07

these i have scaled the simulation here

play16:09

to be a total of 256

play16:11

000 cells by placing hundreds of

play16:13

thousands of water elements

play16:15

the simulation slows down to 10 fps at

play16:17

the lowest and settles at 18.

play16:19

let's see if we can improve on this

play16:22

instead of operating through every

play16:24

single cell one at a time we can

play16:26

implement some multi-threading to

play16:27

operate on multiple cells concurrently

play16:29

and cut processing time to accomplish

play16:32

some simple multi-threading i split the

play16:33

world into columns and execute threads

play16:35

which first process

play16:37

all odd numbered columns and then once

play16:39

those have finished the even numbered

play16:40

columns

play16:41

this ensures that they do not attempt to

play16:43

modify the same element simultaneously

play16:46

we can check our baseline simulation

play16:48

with multi-threading and i found that

play16:50

the frames per second dropped to only 40

play16:52

and settled at 45. this is a significant

play16:54

improvement

play16:56

you may have noticed some step-like

play16:58

formations around the column borders

play16:59

i added some randomly generated offset

play17:01

to the columns positions each frame and

play17:03

this eliminated the issue

play17:08

often times elements will come to a rest

play17:10

and due to their movement behavior

play17:12

keep trying to enter the same occupied

play17:14

cells and do not move at all but are

play17:15

still fully processed

play17:16

every frame in this clip i set the

play17:19

elements to change to the color blue

play17:21

if they have moved since last frame and

play17:23

red if they have not

play17:24

we can see that once the elements find a

play17:26

resting point the vast majority are

play17:28

trying to move down into the side

play17:30

performing thousands of queries to the

play17:31

matrix to find an open space

play17:33

but each time getting the same result

play17:35

and staying in the same cell

play17:37

to eliminate these unnecessary

play17:39

calculations i split the world into a

play17:41

grid of chunks

play17:42

each chunk holds two states whether the

play17:45

chunks contents should be processed

play17:46

this frame and if they should be

play17:48

processed next frame

play17:49

at the start of each frame the should

play17:51

step next frame flag is set as the value

play17:53

of should step this frame

play17:55

and should step next frame is set to

play17:57

false

play17:58

essentially this means we assume that a

play18:00

chunk will be sleeping next frame

play18:02

and rely on its contained or neighboring

play18:04

elements to alert the chunk

play18:05

if it should awaken the next frame as

play18:08

each of the elements attempt to step

play18:10

this frame

play18:11

they check that should step this frame

play18:13

flag on the current chunk they belong to

play18:15

which is based on their current

play18:16

coordinates if it is false then the

play18:19

element stops processing at that point

play18:21

and saves resources

play18:22

whenever an element has something

play18:24

happened to it like being set on fire

play18:26

or changing coordinates the element will

play18:28

report to its chunk that it should

play18:30

process all contents next frame

play18:33

but how will a sleeping chunk ever awake

play18:35

if all the contents do no processing and

play18:37

have no opportunity to wake the sleeping

play18:39

chunk

play18:40

if a new element enters the chunk it

play18:42

will wake the chunk

play18:43

if an element reports to its chunk that

play18:45

it should process next frame

play18:47

and the coordinates are on the border of

play18:49

a chunk it will wake both adjacent

play18:51

chunks

play18:52

the chunk classes are very simple and do

play18:54

not have any knowledge of the elements

play18:56

within their bounds

play18:57

the elements simply tell the matrix

play18:59

wrapper class to alert the appropriate

play19:01

chunk

play19:01

based on its coordinates now let's try

play19:04

our baseline simulation again with both

play19:06

of these optimizations and see what our

play19:08

frame rate is

play19:09

in this case the fbs dropped to 50 at

play19:12

the lowest and settled at 60.

play19:14

given that my computer is a few years

play19:16

old there are over 250 000 cells and the

play19:18

drawing is currently unoptimized i am

play19:20

very satisfied with the stress test

play19:25

i have had a few people ask how i

play19:26

perform the drawing logic for this many

play19:28

cells

play19:29

i'm using the debug draw tool in libgdx

play19:31

to draw each square which is a terrible

play19:33

way to do this

play19:34

i am working on boarding this simulation

play19:36

to c plus and sfml

play19:38

so far it seems the best way to perform

play19:40

the drawing is to create a texture

play19:42

and update each pixel's color in the

play19:44

texture every frame for the elements

play19:46

which have moved

play19:47

and then drawing the texture as a sprite

play19:49

this is much better as there is only one

play19:51

draw call and you can process the sprite

play19:52

with custom shaders as well

play19:54

although i have yet to fully test this

play19:55

method i have spent a good amount of

play19:58

time trying to integrate the box 2d

play20:00

library with the simulation but have had

play20:02

limited success

play20:03

i hope to solve some of these problems

play20:04

moving forward and make another video

play20:06

explaining how to accomplish this

play20:09

as a review for the game loop here is a

play20:10

flowchart of what the simulation does

play20:12

each frame

play20:13

on startup we execute some setup logic

play20:16

such as creating the matrix the columns

play20:17

for the multithreading the chunks and

play20:19

some ui stuff

play20:20

each frame we reset the chunks create

play20:23

and launch our threads for processing

play20:24

the elements

play20:25

process explosions which are placed into

play20:27

the explosion queue during the element

play20:29

processing

play20:30

draw all the elements and then repeat

play20:32

this forever

play20:36

i have been working on this simulation

play20:37

for the past two years on and off

play20:39

i am not a professional game developer

play20:41

and this video merely explains the way

play20:43

that i was able to solve these

play20:44

challenges given my level of skill and

play20:46

knowledge at the time

play20:48

there very well may be better ways to

play20:49

accomplish the things talked about in

play20:51

this video

play20:52

and i would be grateful to hear them if

play20:53

you have any questions or would like

play20:55

additional details on any of the topics

play20:56

in this video

play20:57

ask in the comments and i will be glad

play20:59

to answer your questions i have provided

play21:01

a good number of links in the

play21:02

description to code samples and other

play21:03

resources i used while solving these

play21:05

problems

play21:06

i hope this information was useful to

play21:08

you and thank you for watching

Rate This

5.0 / 5 (0 votes)

Ähnliche Tags
Falling SandSimulation CodingCellular AutomataGame DevelopmentJava AppletsTechnical Challenges2D MatrixElement BehaviorPerformance OptimizationGame Physics
Benötigen Sie eine Zusammenfassung auf Englisch?