środa, 9 września 2015

Ludum Dare 33


  • What is Ludum Dare?
  • What I love about Ludum Dare?
  • What I hate about Ludum Dare?
  • Ludum Dare's survival guide
  • Ludum Dare 33: how it went
  • My entry in comments
  • Other entries
  • What's next?

What is Ludum Dare?


Ludum Dare is a contest where you are supposed to create game on given theme. There are two version of the contest: Compo and Jam. The contest takes place every 4 months.

Compo lasts 48 hours. During Compo you must do all assets yourself - code, graphics, sounds. You must work alone.

Jam last 72 hours. During Jam it is allowed to use assets provided by third parties. It is allowed to work as team.

Proposed themes must survive the "Slaughter". During "Slaughter" people vote which themes should have a shot at being a theme for current edition. The week before Ludum Dare people vote at the themes that survived "Slaughter". The people favourite becomes the theme.

What I love about Ludum Dare


Ludum Dare is big. Every edition produces 2500+ games. That's a lot! Probably more than you have friends on Facebook.

I always go for Compo contest. These are crazy 48 hours. After the end of it you don't want to turn on PC or any electronic device - you are so fed up with sitting and coding. You are exhausted but at the same time you are happy. Two days before you had nothing and now... you have a game! Ludum Dare proves how much can be done during 48 hours if you are determined enough. The limits you thought you had are not broken, they are smashed to the ground.

On the side I am really curious how people survive jam(72 hours) if I am literally destroyed after compo (48 hours). I guess the pace must be a little slower and using done assets must be helpful as well.

What I hate about Ludum Dare?


  • No honest opinion
  • Graphic over code
  • No reply

No honest opinion

I never do good at LD. It's ok, no hard feelings. Regardless of the results there is something I really dislike about LD. I hate that everybody comments the games the same way: "good game". Nobody will say "your game sucks". If it does suck just say so. No constructive criticism. Just game developers "jerking off" to each others creations. After judging time the notes comes back and it turns out your game is nowhere near being a "good game".

Graphic over code

Other thing that makes me go insane are standards of people judging LudumDare entries. For me the small web based games are about two things: idea and gameplay.

Gameplay:
If gameplay is good, if the game gives you fun it doesn't matter that it looks bad. It's ok because you receive that undefined happiness. The happiness that you felt playing tetris or mario for the first time. You don't know why is it fun - it just is.

Idea:
Most of guys competing in Ludum Dare won't be able to make games for living. They won't be able to spend big money on their games. As a consequence of that they won't be able to hire professional artists or composers to work on their game. Therefore there are bound to lose against serious game developers. So if you can't out-force your competition you need to out-smart it. Introduce a great idea. Implement a mechanic that will make people go "wow". Show that games can be more than endless pile of FPSs, RTSs and RPGs.

The problem for me is that during LudumDare community tends to give too much attention to art. You can have great game but still with weak art you are going nowhere. Make a simple game with reused idea but with above-average graphics and community will shit rainbow. The worst of the worst for me is when creators use pixel art. People will rate it high not because the art itself is good but for the sake of "pixel-art" alone.

The other aspect is how much impact the art and coding have on a game. You mess up a piece of art people probably won't notice. Mess up a piece of code and your game doesn't work. Were it to happen next twenty comments you will be receiving are hateful messages that "your game isn't working". So yes I believe that coding deserve more recognition than art. On the other hand why should I even try to do something bigger and better if I can get better result with smaller risk just by investing in better art?

No reply

Comments under your entry are for me as important as final results. Sometimes somebody gives you a longer review than "good game". Even if you disagree with some of the opinion in the review there's no reply option. It may seem as little or no pain but from developer's point of view it's important to prove to the public why you did things the way you did or why you disagree with someone's opinion.

Ludum Dare's survival guide


  1. Everything during LD is limited by time.

    You must be aware that once you are done you will feel like a lot of things could have been done better. One rule that you should always keep in mind is NO TIME. No time for good graphic, no time for having a well edited music, no time for epic story-telling. No time, no time is pretty much an anthem for LD.

  2. Gameplay first.

    If you have no gameplay, you have no game. Make provisional graphic in form of square, circles and rectangles. Make sure that mechanics work then think about making nice sprites.

  3. Know your tools.

    Ludum Dare is no place for learning your tools. Learning new things is generally hard. It's even harder with a time ticking. Learn your tools before LD otherwise you will be doomed.

  4. Quality in screentime.

    If you have created a pretty sprite try to reuse it as much as you can. On the other hand if something will appear only once on the screen during the whole game you don't need to bother with detailed drawing.

  5. Dreams to dust.

    As you think about making a game you think big. There will be dragons and mages, there will be crafting, spell casting, dialogues. But then the time starts passing by and you have to say farewell to most of your ideas. So if your concept turns out to be too big to be finished in given time be ready to remove some of the planned contents. Doing this may be sad but again NO TIME.

  6. Coding requires thinking.

    Once you get tired you will start doing lots of stupid mistakes that require a lot of time to be fixed. If it were to happen stop coding, do some graphic and return to the coding the next day. This pretty much says: coding first everything else later.


Ludum Dare 33: how it went


My entry:
http://ludumdare.com/compo/ludum-dare-33/?action=preview&uid=34334

Preperations:
I wanted to make my entry using Unity. I did few tutorials but I haven't done anything on my own in it. As Ludum Dare is not a place to learn something new I decided to use Stencyl instead. I consider this a bitter disappointment.

What went right:
Definitely time management. I had my game finished and working 5-6 hours before deadline which is great. I think that whole production process was doing really well as well:
  • Day 1 - gameplay mechanics, map graphic, half of character graphics
  • Day 2 - opening and closing menus (potions, upgrade, dress-up), special attack, second half of character graphics, main title screen, music, Newgrounds achievement


I'm quite happy with the result. Looking at other entries I think I'm at top when it comes to the length of code written for the game. It's the first time I have made a turn-based rpg. The fact that during this LD I have done a genre that I had no experience before is really satisfying. At the same time I must say that I had some preparation before Ludum Dare because I had written some basic turn-based rpg in order to solve problem from Stencyl forums. I haven't used any of code from that project but I had still in my mind the idea of how the code for the whole game should be implemented.

What went wrong
I don't think anything went extremely wrong. Most of these are problems along the way, natural for process of creation.
  1. Create list vs empty list

    Stencyl can't empty list that hasn't been created. I have committed this "error" before so finding it wasn't that painful but still it's a waste of precious time. I put "error" in brackets because I feel Stencyl should create a new list on its own as variable is declared instead of forcing a users to do it themselves.

  2. Werewolf's nose

    I'm not an artist but I had really hard time making a good werewolf/dog nose. Despite my squarish graphic.

  3. No combat music

    There was no problem with sfx as I used like always:
    http://jfxr.frozenfractal.com/#

    Then I was thinking about doing some music for map screen and combat screen. I open my bookmarks and... I don't find the bookmark I was looking for.

    As it later turned out the site I was looking for was:
    http://www.abundant-music.com/

    In the end I managed to find another random music generator. It was enough for map theme but no good for combat theme. Even if I had access to the site during LD I doubt if I managed to create combat music as I'm lacking in music terminology. Because of this I wouldn't be able to configure seed numbers to generate music I want. Learning what bridge, harmony and bass are is one of the position on to-be-done-before-LD34 list.

  4. Mirror image

    In my game I had models for arms, heads and bodies of character created for player. I failed to notice that if I wanted to use mirror image for enemies the graphic must have center in the center of image. This forced me to add offset after doing the mirror image => another unnecessary time-waster.

  5. No rotate around point in Stencyl

    I was shocked to find out that such basic function isn't implemented. Since LD doesn't give you time to do things nicely, using math, I made it using "works about right" logic.

  6. Given up idea - Special attack

    As time was passing by I was forced to give up idea of special attacks. I had this concept that player would have a special attack based on the head they are wearing. So if I was wearing a zombie head I would have a chance to poison enemy, vampire - drain life, werewolf - bleeding/critical damage. The problem itself was not only to implement special attacks but what was even worse I would be forced to mark status of nerfed enemies by adding icons. Another lines of code, another graphic and time is ticking.

    Enemies were supposed to use specials as well. AI of enemies was supposed to be wiser. In plans AI analyzed such things as: if I am able to kill player in this attack using .... type of attack and what's the best way to do it (chance check), how to counter player. The idea of simpler but working approach crossed the chances of smart AI.

  7. Gradation of enemy and equipment/body parts

    Probably could have been done better as both enemy and equipment gradation was done more random than rational. With enemies there was at least some idea behind it:
    • 1st enemy - dead in 2-3 attacks, no threat
    • 2nd enemy - dead in 4-5 attacks, kinda stronger but still no threat
    • 3rd enemyh - equal to player from begining of the game
    • 4+ - just stronger, no deeper thoughts

  8. Bug found

    There was one bug found but it was easy to repair.

  9. Stnecyl refused to import music

    No idea why. Stnecyl proved to be a picky b*tch in the worst possible moment.

My entry in comments


Biggest complains:
  1. Bad art - I must say I like my art for this game. Squarish characters were intended and I'm satisfied with their looks. Only exception being Scientist which really could have been done better. Probable some animations would be fine such as character blinkings.

  2. Make different animations for different attacks - I don't know how it could have been done. I wanted my monsters to look like mechanical puppets and I think I achieved that. Should I make a player uppercut the enemy if the player chose "strong attack"?! - the idea sounds extremely stupid.

  3. No combat music - I fully agree with people complaining.

  4. No story ending - this was single complain but I fully agree with it. I had a nice storyboard which introduced a protagonist and villian. I should have done similar one for the game ending.

Other entries


Most popular idea among entries involved being a godzilla-style monster and killing something. Fun if you play 2-3 entries but after 10th one it gets boring.

Some entries wanted to take moral approach but in most cases I don't think they did it good enough.

Most common monsters: undeads(zombies, vampires), infected, dragons, godzillas (big monsters), extraterrestrials, demons, killers

For best entries after 50 go to:
http://ludumdare.com/compo/2015/08/26/you-should-play-this-after-50-games/

I have played 75 games so at this point I can add these entries to the best-of list:

Slasher:
http://ludumdare.com/compo/ludum-dare-33/?action=preview&uid=8745

Lil' Drake
http://ludumdare.com/compo/ludum-dare-33/?action=preview&uid=18768

Eat Sheep & Die
http://ludumdare.com/compo/ludum-dare-33/?action=preview&uid=40797

What's next?


I will be describing my LudumDare entry because I think it's good starting point for anyone considering doing a turn based combat.

I hope to do a non-jam game as I hadn't done anything of sort in a while. As art has always been a source of complain I decided to start learning blender. Regarding the music I know I will need to catch up with music terminology and play around with music generators but I am not sure when I will exactly do it.

poniedziałek, 10 sierpnia 2015

#Stencyl: Zerg terrain infection

Part 4: Spreading the infection

Topics:
  • Observation of infection
  • Algorithm of infection spreading
  • Implementation of algorithm
  • Infection over time
  • Extras: counter-infection algorithm
  • Extras: making infected area more interesting

Behaviors used in part IV

Behavior Events:
  • infection_behavior (scene behavior)
    • infect
    • infection_step
    • when <a> pressed
    • when <f> pressed
    • do every <1> seconds

Main variables:
  • infection_behavior
    • infected_list - List; stores as elements infected tiles actors that make edge of infected area
    • temp_list - List; stores the copy of <infected_list>
    • checked_tile - Actor; element of <temp_list>
    • auto_infect - Boolean; true if infection should spread over time

 

Observation of infection spreading


Before jumping to algorithm itself we need to make some observations about the infection process
  • Only edge of infected area is able to infect further tiles. There is only need to track tiles that make edge.
  • After infecting edge tile becomes a tile inside infected area surrounded from four sites by infected tiles (since there are no tiles which can resist the infection).

Edge tile(green) infects tiles around it(blue). Right: after infection tile is surrounded from all sides by infected tiles.


Algorithm for infection spreading


Algorithm for infection (single iteration):
  • Make two lists. First list <temp_list> stores the currently infected edge tiles(pre infection). Second list <infected_list> stores the newly infected edge tiles (post infection).
  • For each tile of current edge try to infect tiles around it. If you encounter uninfected tile infect it and add tile to the list of newly infected edge tiles.
  • Copy elements from newly infected edge tiles to currently infected edge tiles.

Implementation of algorithm
       -> infection_behavior -> infection_step
       -> infection_behavior -> infect
       -> infection_behavior -> animate
       -> infection_behavior -> when <a> pressed

Trigger event <infection_step> does a single iteration of infection. It is called when you press <a>.

At first we set pre-infection edge tile list <temp_list> and post-infection edge tile list <infected_list>. <infected_list> is empty at the beginning.

Then we go through each tile actor of pre-infection edge tile list. Set that actor's animation to animation of being surrounded by 4 infected tiles. Send information to <infect> trigger event that you would like to infect tiles around you. We need to set <checked_tile> before every <infect> trigger event because <checked_tile> is changed upon execution of <infect> trigger event.

<infect> trigger will determine if tile is infected or uninfected. If it's uninfected it will infect it - set proper value in matrix <ground_list> (matrix responsible for keeping status of tile; Part 3) and add the newly infected tile to <infected_list>.

After infection process is done we need to update animation for post-infection edge tiles based on their surroundings. It is done by <animate>.

We add event <when <a> pressed> so that we can call manually single iteration of infection.

Infection over time
       -> infection_behavior -> when created
       -> infection_behavior -> do every <1> seconds
       -> infection_behavior -> when <f> pressed

Infection over time is nothing more than calling <infection_step> once every <some time>. We add <do every <1> seconds> to achieve it. Since we don't want infection to spread until we tell it to do so we add <auto_infect> condition. In when created we set value of <auto_infect> to false. Value of <auto_infect > can be changed to <true> upon pressing <f>.

Events allowing infection over time

Extras: counter-infection algorithm

Sometimes we want for ground to regain lost ground. For example if the infecting race lost supply that allowed the infection. The biomass on the infected tile is dying so neutral ground regains it. The algorithm for this is very similar to that of infection.

Algorithm for counter-infection (single iteration):
  • Make two lists. First list <temp_list> stores the currently infected edge tiles(pre counter-infection). Second list <infected_list> stores the newly infected edge tiles (post counter-infection infection).
  • For each tile of current edge. See if around it there is a tile that has four sides infected. If yes and it's not in <infected_list> add it.
  • After you are done with creating <infected_list> for each tile of currently infected edge set it to neutral.
  • Copy elements from newly infected edge tiles to currently infected edge tiles.
  • Animate the edge tiles based on their surroundings.

Extras: making infected area more interesting

We have infected area but once it gets big we will end up with big boring purple plane. If infected area is big enough we can build some objects inside infected area. The objects themselves can be build up from tiles.

This will require checking once in a while values in <ground_list> to answer the question if we have enough infected tiles to build. Since it's only graphical effect with no influence on gameplay we don't need to do it often.

Infected area with added pools of green goo(sized 3x3) and grouped infected tiles(2x2).

To choose other part follow the link:


czwartek, 6 sierpnia 2015

#Stencyl: Zerg terrain infection

Part 3: Creating tiles, infecting first tiles

Topics:
  • Adding new matrices
  • Adding tiles actors
  • Marking tiles occupied by buildings
  • Infecting tiles around buildings
  • Changing animation of infected tiles
  • Extras: Is it ok to place building using tile logic
  • Extras: Infect area outside of screen 

Behaviors used in part III

Behavior Events:
    •  main scene behavior (scene behavior)
      • when created
    •  tile_behavior (actor behavior)
      • index_declare
    •  building_behavior (actor behavior)
      • placed_on_map
    •  matrix_support_behavior (scene behavior)
      • rectangle_fill_by_index
    •  infection_behavior (scene behavior)
      • infect
      • animate
      • animate_me

    Main variables:
    •  main scene behavior (scene behavior)
      • tile_list - List; as elements contains tiles actors
      • tile_size - Number; width/height of tile, often used to translate position in pixels to position in matrix coordinates
    •  matrix_behavior (scene behavior)
      • ground_list - Matrix; contains value which corresponds to status of tile (uninfected, occupied by building, infected)
      • tile_list - Matrix; contains indexes to list <tile_list> in <main_scene_behavior>, indirectly stores tiles actors
    •  tile_behavior (actor behavior)
      • index - Number; actors knows that he is being stored in list <tile_list> in <main_scene_behavior> as <tile_list[index]>
    •  infection_behavior (scene behavior)
      • x - Number; X position in <ground_list> matrix
      • y - Number; Y position in ground_list matrix
      • return - Number; status of tile before the infection
      • infected_list - List; stores as elements infected tiles that make edge of infected area
      • top, down, left, right - Number; status of neighbouring tile, needed for choice of animation
      
    Adding new matrices
           -> main_scene_behavior -> when created

    We will need two more matrices: <ground_list> matrix and <tile_list> matrix.

    Values in <ground_list> matrix will correspond to status of tile
    • 0 - tile is uninfected and free
    • 1 - tile is occupied by building (we consider them to be infected as well)
    • 2 - tile is infected

    <tile_list> matrix will indirectly contain tiles actors. In <tile_list> matrix as elements there will be indexes pointing to list <tile_list> of <main_scene_behavior>. <tile_list> of 
    <main_scene_behavior> is the list that will contain tiles actors themselves.

    If you find indirectly placing actors into matrix to be confusing read section "Storing in matrix something different than numbers" from "Matrix behavior" post: 
    http://t4upl.blogspot.com/2015/08/stencylmatrix-behavior-topics-download.html

    We add matrices in <when created> of <main_scene_behavior>. We fill them both with 0s so that we can use <set_value_by_index> later on. Size of places in matrices to be filled is equal to number of tiles.

    We wrap whole creating of matrices in <do after> because Stencyl doesn't allow to use trigger events of scene behavior in <when created> of scene behavior.

    Filling matrix with 0s is required because:
    • It's good to have some initial state, it makes debugging easier
    • <set_value_by_index> of <matrix_behavior> requires that record exists before it will set specific value in that record

    <when created> of <main_scene_behavior> completed with part of code adding <tile_list> and <ground_list> matrices.

    Adding tiles actors
           -> main_scene_behavior -> when created
           -> tile_behavior -> index_declare
    In <when created> we create tile actors so that they cover the screen. As we create them we add them to list <tile_list> of <main_scene_behavior>. We let the tile actor know under which index is he hold in the list by setting <index>.

    The if statement allows us to go to the next row while creating tile actors.

    If you find placing actors tiles to be confusing read section "Creating a matrix of cards" from "Match game" post:


    <when created> of <main_scene_behavior> completed with part of code responsible for creating tiles actors.

    After adding the tile we force it to update <tile_list > matrix with its index by triggering <index_declare>.

    Information about index which holds the tile actor is given to tile so that it knows it. Since it's the last actor added to the list it's index = Length of List - 1.


    Tile actor calculates where he should put information about its index in the matrix based on it's <X position>, <Y position>, <tile size>.


    Tile actor puts the information into the matrix since it knows everything it needs to do it.


    Trigger event <index_declare>. Tile actor using information about its position and <tile_size> learns where to put its index into the matrix. The tile actor then puts the index into the matrix.


    Marking tiles occupied by buildings
           -> building_behavior -> placed_on_map
           -> matrix_support_behavior -> rectangle_fill_by_index

    After we placed the building we need to update <ground_list> so that it will mark the tiles covered by building. We already have a trigger event that is executed as building is placed called <placed_on_map>. We add part responsible for matrix update.

    The added code sends information about corners of the building as matrix positions to <matrix_support_behavior>. Upon triggering <rectangle_fill_by_index> information is used to fill matrix with 1s in rectangle pattern - this way marking in matrix the tiles covered by building.


     Send information from <placed_on_map> about corners of the building as matrix coordinates to <matrix_support_behavior>


    Trigger event <rectangle_fill_by_index> fills matrix <ground_list> with 1s in area described by corners.

    Infecting tiles around buildings
    -> building_behavior -> placed_on_map
    -> infection_behavior -> infect

    After placing the building we want to infect tiles around the building. To do this we add another part to <placed_on_map> trigger event. We go through each tile covered by the building and if this tile makes edge of the building it tries to infect one of the tiles around it (left,right, above, or down of it depending on edge).

    Trying to infect means sending crucial information (in this case position in <ground_list> matrix) to infection_behavior which will do infection upon triggering <infect>.


    Building checks its tiles. If they are edge tiles they try to infect tiles around it. Back of the arrow shows the source tile on the edge and head of the arrow points the tile that is being infected.


    <placed_on_map> with added part that checks tiles covered by building in order infect tiles around it.

    <placed_on_map> doesn't infect tiles. It just sends request for tile at <x>, <y> position in <ground_list> matrix to be infected. The infection itself is handled by <infect> of < infection_behavior>.


    <infect> of < infection_behavior> - trigger event responsible for handling the infection of tile at <x>, <y> position.

    Before going to infecting tile itself let's make one thing clear: we don't want to infect tile that's already infected because it doesn't make sense. The second thing is that we assume that tiles covered by building are infected. If player doesn't see them it doesn't matter if they are infected or not so it's ok to assume such thing.

    As first thing we assume that tile we are willing to infect is already infected (<return>=1). We ask if position of tile we want to infect is inside boundaries of the map. If the tile position is outside of the boundaries <return> remain equal to 1 and nothing happens because later if requires <return> = 0.

    If tile position is inside the boundaries of the map we ask what's the status of the tile by asking the value in <ground_list>. If it's equal to 0, meaning uninfected, (in code: if <return> = 0) we proceed with infecting it.

    We want to know which index of the list <tile_list> of <main_scene_behavior> stores the tile actor we are about to infect. We learn it by asking <tile_list> matrix of <matrix_behavior> what value it holds at <x>, <y> position. Now we can get newly infected tile actor as element under the index we learned in <tile_list> of <main_scene_behavior>.

    We change animation of the newly infected tile actor to <Animation 0> which is one of the infected tiles animations (fully infected square). We add newly infected tile actor to <infected_list> - we will need this list later. Since we have infected the tile we need to mark the fact that tile has changed status from uninfected to infected. We do it by setting value in <ground_list> matrix at <x>, <y> position to 1.
     
    Changing animation of infected tiles
           -> infection_behavior -> animate
           -> infection_behavior -> animate_me

    What we want to do now is add some variety to our infected tiles. We will do it by changing animation of the tile based on its surrounding. There are some basic states that tile can have. Once we have basic states all other can be achieved by rotating the basic states.


    States that tile can have based on its surrounding. Brown - uninfected, purple - infected.
    (1) 4 sides uninfected, (2) 1 side infected (3) 2 sides infected on opposite sites (4) 2 sides infected on neighbouring sites (5) 3 sides infected (6) 4 sides infected

    For animating tile actor we will use <animate> from <infection_behavior >.

    Side note: if tile has as its neighbour tile outside of the screen we assume that this tile out-of-boundaries is infected.
      
    Trigger event <animate> from <infection_behavior >.

    The big while at the beginning let us do animation change for each element of <infected_list>. This means we will perform following code for each edge tile of infected area.

    The neighbourhood of tile is represented by variables <top>,<down>,<left>,<right>. They are 0 if neighbouring tile in given direction is uninfected and 1 if it's infected. There is a variable <sum> which says how many tiles around current tile are infected. <sum> makes choosing animation simpler but is not obligatory for solving the problem itself.

    Once we have chosen the edge tile we ask if neighbour in chosen direction is on map. If not we consider it to be infected and set proper direction variable to 1. At the same time we add 1 to <sum>. If neighbouring tile is on the map we ask its status by getting value from <ground_list> of <matrix_ behavior>. The tile is infected if value in list equals 1(covered by building) or 2(infected but not covered by building). If statement ( >0) checks both of the possibilities. If the tile is infected we set proper direction variable to 1 and we add 1 to <sum>.

    After we get all info about status of neighbourhood we trigger <animate_me> which will set proper animation to the tile based on values of <top>,<down>,<left>,<right>.


    Trigger event <animate_me> uses values of <top>,<down>,<left>,<right> to set animation to edge infected tile.

    Extras: Is it ok to place building using tile logic

    In part II we learned how to place the building. The approach we used didn't use tiles. This part presents alternative approach to problem of checking if it's ok to place building using tiles and <ground_list> matrix.


    Example of trying to place building(green frame) over building already on map. On right it is visible how the <ground_list> matrix looks like as we try to place the building; 0 - free place, 1- covered by building

    In alternative approach we check if in area we are about to place the building (green) are any occupied places (represent by 1). If yes that means we can't place building. If no it's ok to place building and we place it.

    Pros: The approach from part II is ok if our buildings are rectangles or we can assume that they are more or less of that shape. This generally works. By using getting values in matrix we can place buildings that are triangles or other more exotic shapes.

    Cons: As we place building in this approach we will need to associate with each type of building a shape equivalent in matrix. Both setting the building and checking if its ok to place are therefore more difficult.

    Extras: Infect area outside of screen

    We decided that area outside of screen is considered infected. This is ok but we can do it better by not assuming anything. All we need to do is add 2 additional columns and 2 rows to the matrix so that they can represent tiles outside of screen. This however creates offset between actor position and its position in matrix what can be painful to use.


    Left picture shows map with building placed in top left corner of the map. Middle - matrix as used in project. Right - matrix with added record to cover outside of screen, this causes (1,1) offset.

    To choose other part follow the link:

    niedziela, 2 sierpnia 2015

    #Stencyl: Matrix behavior


    Topics:
    • Download
    • Behavior's history
    • Matrix in matrix behavior
    • Using matrix behavior
    • List of trigger events
    • Storing in matrix something different than numbers
    • Implementation details


    Download
    <Matrix behavior> can be downloaded together with <Zerg terrain infection> project:

    Behavior's history
    There are many cases when people need 2D lists or matrix for their project. Stencyl does not support in any way such problem.

    My first prototype for matrix behavior was created when I need to make a map for generating maze.

    Generating maze project:

    The behavior worked but whole set-up was rather chaotic. As I went on with Zerg terrain infection project I decided to make matrix handling more coherent.

    Zerg terrain infection project

    The behavior still needs some cosmetic upgrades such as more intuitive arguments names. In current version the behavior is fully functional. I plan to develop matrix behavior in future in order to make it more user-friendly. The next version of behavior will come with presentation in form of Stencyl project.

    If you need a function and it hasn't been implemented let me know.

    Apparently there is another 2D list extension but I haven't used it and I doubt if there is any documentation on how to use it:

    Matrix in matrix behavior

    Matrix is 2D list made for storing number and numbers only. It has x and y direction. Set of entries with the same y and different x is called record. Matrix is limited in x direction and unlimited in y direction meaning you can add any number of records you like. Position in both x and y direction are indexed starting from 0.


    Picture of matrix. Red numbers represent single record.

    As we create matrix we name it. There can't exists two matrix with the same name - name must be unique. The newly created matrix is the added to the list of matrices. That means we can refer to matrix either using its name or using index which holds reference to the matrix. Most trigger events are available in two options _by_name or _by_index. Using _by_index is quicker but is less user friendly.

    Matrix knows:
    • its name
    • its index in list of matrices
    • its number of records
    • limit in x-direction


    Picture shows list of matrices with 3 matrices.

    Using matrix behavior

    Communication with <Matrix behavior> is based around setting attributes for <Matrix behavior> using block: <for this scene, set <> to <> for behavior <Matrix behavior> >. Then we trigger event according to what we want to do.

    If what we did should return something, for example value at (x,y) position we can get it using block: <for this scene, get <> from behavior <Matrix behavior> >.

    In <Matrix behavior> there is a special inactive <when created> event that allows quick copy-paste from <Matrix behavior> to other behaviors.

    List of trigger events

    General rule is that _by_index triggers don't check if input values makes sense. They assume you know what you are doing. _by_name triggers tend to check input values and print proper error to the console if they find something wrong.

    IN - attributes you are ought to change while triggering the event
    OUT - attributes you can get after you trigger the event

    In <Matrix behavior> the following trigger events has been implemented:

    • add_list - create new matrix
      • Additional information:
        • You can't trigger scene events in <when created> events of scene bahavior. You need to add do after <small time> if you want to use trigger event in <when created> event of scene bahavior.
      • IN:
        • name - name of matrix; must be unique
        • size - size of record in x direction; must be bigger than 0

    • add_record_by_name - add record to the matrix with given name
      • IN:
        • name - name of matrix
        • input_list - list containing record you want to add

    • add_record_by_index - add record to the matrix with index
      • IN:
        • helper_2 - index of matrix; must be unique
        • input_list - list containing record you want to add

    • get_data_by_name - returns info(index, number of records, limit in x-direction) about matrix with given name
      • IN:
        • name - name of matrix
      • OUT:
        • size - limit in x-direction
        • number_of_records - number of records in the matrix
        • matrix_index - index of matrix in list of matrices

    • get_record_by_index - returns record from matrix with given index
      • IN:
        • input_1 - index of matrix in list of matrices
        • input_2 - index of record (index in Y direction)
      • OUT:
        • input_list - list containing a record

    • get_value_by_index - gets value at (x,y) position from matrix with given index
      • IN:
        • input_1 - index of matrix in list of matrices
        • input_2 - X position
        • input_3 - Y position
      • OUT:
        • output - value at (X,Y) in matrix with given index
           
    • set_value_by_index - sets value at (x,y) position from matrix with given index
        • Additional information:
          • It's forbidden to set value in record that doesn't exist.
        • IN:
          • input_1 - index of matrix in list of matrices
          • input_2 - index in x direction
          • input_3 - index in y direction
          • input_4 - value to be set

        • fill_by_name - matrix with given name is changed into matrix of <n> identical records. Each record is filled with the same number.
          • Additional information:
            • requires list_behavior
          • IN:
            • input_1 - number that should fill matrix
            • input_2 - number of records that should fill matrix

        • print_matrix_names - prints into console all names of matrices along with their indexes

        • print_matrix - prints into console matrix with given index
          • IN:
            • input_1 - index of matrix in list of matrices

        Storing in matrix something different than numbers

        Matrix behavior does not allow to put into matrix something else than numbers. In order to store in matrix other type than numbers we need to do it indirectly. We create additional list with objects of type we want and in matrix we keep indexes to the objects.


        Picture shows indirectly placing objects of chosen type into matrix.

        Implementation details

        In reality <Matrix behavior> keeps matrices as lists. X and Y coordinates of matrix are translated to index of list using limit in x direction. If X and Y are coordinates then index [i] in list is calculate as:

        [i] = Y * <limit in x direction> + X

        Matrix as in matrix behavior and matrix as list.

        Matrices are stored in list called <ultra_list>. Attributes of matrix under index [i] in <ultra list> are stored as elements [i] in lists:
        • name - <name_of_lists>
        • number of records - <number_of_rows>
        • limit in x-direction - <size_of_list>

        <Matrix behavior> uses one local trigger event <find_list>. <find_list> searches for matrix with <name> in list <name_of_lists> and returns index of matrix in <ultra_list> as helper_2. If matrix with given name doesn't exist helper_2 is returned as -1.