Structuring Game Design

I’ve been working on a game of mine in Python for a while now, and I just started rebuilding my code from scratch due to new concept designs and want of clean organized code.

Anyway, I decided to make a google doc (not linked for obvious reasons such as secrecy of ideas, etc, etc) and markdown all the dialogue, scenes, basic if loops (phrases such as IF DEATHCOUNT is ONE for example), and now that I realize the scope of my game, I’m wondering what the most efficient/effective way of approaching this planning is. Do I just write plans from beginning to end? Do I structure the most important ideas first?

2 Likes

(my personal opinion)
So the question is, what should be included in the plans prior to making the game?
If you make little plans, inefficiencies and bad code and design may pile up from lack of forethought and overarching structure. If you make too many plans, inspiration and innovation can be stunted, it is more work to get started, and flexibility is sacrificed, potentially leading to unforeseen problems that upend all of the design.
There must be a balance (this also dictates what to include).

I think the YT video “The one thing you need to finish your game” by GMTK is good.
At first, you should just explore and find out what’s best for your game. (Making a prototype to explore gameplay feel is highly recommended, except for text games). Then, once you find the core concepts, you can develop the rest of the ideas (including story, art, code, and other design things) in a plan.

What should you put in your plans? You really shouldn’t be putting in the individual coding structures like functions or conditions (in my opinion), too slow. Writing the code once is better than doing it twice where one is pseudocode. But you probably want to plan out the structure of your game code such as the classes and how the game loop works.
You obviously must plan out the story a lot. And the concepts, core “pillars” of gameplay, scope, hook, summary, etc. are all important.

The point of a plan is to have an overview of what will happen so you aren’t lost, improvising the entire thing.

5 Likes

My current strategy is writing the base classes as modules on replit, and writing the gameplay/scenes/some conditions on my google doc. I’m not doing any art or stuff yet, and the scope is kind of massive.

It’s a RPG game like Undertale (which you prob know), and is sort-of a fangame of Undertale (UT for short). It’s the same characters, some of the same concepts, and a majority of the same mechanics, except I’m redesigning the art, sound, and mechanics/concepts (I’m just recreating it from scratch). I’m changing the map look (I.E. art), the entire storyline, I’m adding characters, increasing the scope to be more free roam, making choices less GOOD or EVIL.

Like UT, I’m separating the base game into 3 types of runs. Pacifist (perfect ending), Genocide (kill everyone + world’s erasure), and Neutral (of which I’m intending for many many variations of). Genocide/Neutral ending are required for THE PERFECT ENDING, as well as a certain amount of luck in randomized values.

The main issue I’m running into right now is I can’t figure out who the enemy is for the pacifict/perfect ending. In Geno/Neutral, you could argue killing monsters (a race, not as like the things that attack you for no reason such as zombies, etc.) is considered bad, but in UT, they attack you first (though they have okay reasons), it to feel unfair towards the player, as they tried to kill you, why can’t you kill them?

So I’m stepping back any battle encounters to your own fault, and in geno/neutral killing any is a perfectly acceptable reason to try to imprison you (I want to branch out into more choices/storylines from there, but that’s a ridiculously massive scope), but in the pacifist/perfect ending, you don’t murder anyone or gain any LV (short for LOVE, which is short for Level of Violence). So how can I rightfully induce any battles?

I noticed it as a major design flaw today as I was writing dialogue, realizing I needed a better planning system. Really, I think the major issue I have is: Too much scope, the fact I want hundreds of total routes, making the replay ability factor sky-high, while keeping flexibility. Since I’m planning dialogue / scenes, it becomes pretty easy to add more, and I have a neat code system designed to keep track of routes completed/current route/everything and anything.

If you have any ideas for my enemy issue, let me know! But back to the topic I asked about, I feel basic conditions MUST be included when planning. When separating dialogue/story based on many many if conditions, that has to be planned right away, as that’s what the story is based on. I’m keeping parts of the routes the same, as that’s forced if I want any possibility of finishing this within the next 2 years or so (for a school final presentation), as well as without hardcoding ALOT of it.

I guess I’m just too imaginative lol. Though I don’t really get what you mean by “pillars” of gameplay. As in core mechanics (such as battle system, movement system)? I have a hook into the game fully planned, the intro mostly planned, and I’m currently on the first part of the game, where I started separating the first battle choices into different routes. (you instant die on fight action and everything resets, but death message changes each time and after 5 times or so you just straight up one shot them, with the message DETERMINATION that flashes for a quick instant). But on the spare choice or flee, how do I determine where I want the game to go. I don’t want to lock out any choices (I want it to be both open and a near rpg at the same time), it just seems nearly impossible to plan out hundreds of different endings dialogue.

Idk, the better question is how do I go about doing this?

Example of how I’m currently writing it:

BATTLE:
IF TURN 1:
- BATTLE TEXT: You are attacked by --------

-CHOICES:

  - FIGHT, ACT, ITEM, MERCY

- IF FIGHT: DEAL player.stats[“player_damage”] DAMAGE (Modified by when Z pressed)
  - ACTIONS.FIGHT_COUNT ADD ONE

IF DEATH COUNT is 4

  CUE SCENE
  ------------------------------

  BATTLE TEXT: ------------------------------

  WAIT UNTIL Z-KEY PRESSED

  FADE TO BLACK

  Dialogue: ------------------------------
  Dialogue: ------------------------------
    END SCENE
    EXIT BATTLE
    game_controller.monsters_slain.append([ROOM, MONSTER, ACTIONS])

  START MONSTER ATTACK				
        ----- Dialogue: ------------------------------
  IF PLAYER DEATH:
      game_controller.track_data[“player_deaths”].append([ROOM, MONSTER]) 				
      PLAYER DEATH ANIMATION
      IF DEATH COUNT is 1 (FORCED): 
            ----- Dialogue: ------------------------------
            GAME Dialogue: Error - Cannot reset, commencing reload...
     IF DEATH COUNT is 2 (FORCED): 
            ----- Dialogue: ------------------------------
            GAME Dialogue: Error - Cannot reset, commencing reload...
    IF DEATH COUNT is 3 (FORCED): 
            ----- Dialogue: ------------------------------
            GAME Dialogue: Error - Cannot reset, commencing reload...
    IF DEATH COUNT is 4 (FORCED): 
            ----- Dialogue: ------------------------------
            GAME Dialogue: Error - Cannot reset, commencing reload…
            GAME Dialogue: DETERMINATION

(This is just a small snippet of many google doc pages of planning. It’s in code format just to seperate it lol)

3 Likes

I slightly misinterpreted your first post (because I didn’t know what the game was about).
I see that the problem is scalability, as you realize that though you want to create many routes, but this will obviously be a challenge. So, this seems to be one of the main problems in development.

You already have a system for managing routes, etc. But the problem is creating and planning them. I would recommend maybe using an actual diagram software to easily create and see your routes.
Code reusability is key here, and this seems like a good place for many, many functions and classes. I would still recommend against putting raw code in your google docs because you’ll have to type it again later.
From the docs snippet, here are suggestions condensing and improving the format of the planning:

  • put “death()”, “game_dialogue(“msg”)”, etc. since you’re going for ‘code-like’ formatting anyway. Looks nicer in my opinion.
  • put actual variables and conditional statement code
  • I actually prefer a plain txt file instead of google docs because of default monospace and no autocorrect. Indentation is very important for the formatting here.
  • replace the long game_controller line with a function or method. You’re probably making one for the actual code. Also for END SCENE, etc.
  • half of the snippet looks repeated, why not just use a dict of death counts to dialogue?

Some potential classes I’d recommend having are to replace the [ROOM, MONSTER, ACTIONS] list (look into dataclasses and namedtuples), and a Dialogue class to encapsulate a “session” of dialogue.

You want to avoid repetition as much as possible by using functions, changeable classes, dicts, etc. If you do this, you could even create a system to load in entire scenes or battles from a text file, because each could be represented by a single object. Avoid hardcoding too much stuff.

class Dialogue: ...
defeat_1 = Dialogue(...)
defeat_1.start_or_whatever()

Maybe a “Battle” class too. So you can easily store what the player has been doing.

class Battle: ...
b = Battle(args)
b.start()
player.battles.append(b)

I have not played Undertale, but for your story problem, maybe make a small number of “surprise attacks” that a pacifist might have to defend and struggle to get out of without killing. The enemy or battle doesn’t always have to involve violence, maybe they are cutting off your potential paths in other ways and you have to stop them?

4 Likes

Current Code Setup: https://replit.com/@JonathanNitzan/Storyshift-v01

  • Let me know if you notice any issues with my code or code design!

A: Wouldn’t it be more efficient and effective to design the game to keep track of every action/interaction the player has had, then have a map dictionary or 2D array to keep track of dialogue per current key (would just compare current route interactions/actions to saved dict of all possible routes)?

B: I was kinda dumb not giving enough reference (lol, I put a link to the replit at the top). The game_controller is my variable defined in main for the imported module Controller, which contains the class Controller(). Since I prefer to keep my main.py file as simple as possible, I decided to just later define a game_controller.update sort of function that runs each tick, with each core classes being called upon in the game_controller instead of main. Main just runs the updates and runs the operations from the base (I.E. checks events run and conditions for event to run based on class function, then runs them).

C: Can you give an example of these “surprise attacks”? In a game that is based of choices, how can you simulate it without actual battle? I just realized since you haven’t played Undertale, you wouldn’t have quite realized what I meant. In Undertale, the player has the ability to “reset” which basically means when they die, they go back to their last “savepoint”. UT uses this in a unique way, creating it black and white, because you had technically infinite tries to get it right. Battling also consists of the ACT option, which contains various options (4 options max), in which a various order allows for a optimal ending. There is also a special value called the FUN value, which changes some hidden encounters, and past runs can also effect your encounters/dialogue. A single monster in the game has semi-complete memory of resets, for which the reason is unknown. In the original game, this monster is lazy, and is the final boss if and only if you kill everyone else. In other routes, they just remark on your choices. To rephrase myself, the “battles” are more accurate as “encounters”, where I’m running into creativity issues. If all the monster does is talk, then it’s just dialogue, which doesn’t have much playability, at the same time, I want to give them a valid reason to attack. Death counts in this case don’t really matter, it’s more of a reference for myself. Since if you attack, the first attack kills you no matter what (for the first 4 deaths), it’s really more of a reset number. I want to reuse the same idea later ideally for hard boss battles (if death_count to boss = x, then dialogue = “”).

D: I don’t really get what you mean by half the snippet being repeated, when copying it down, I replaced names / essential dialogue with ----------. However, there are some parts where I just hadn’t figured it out yet and put ---- as a placeholder.

Finally, I’m not going for code or psuedo code here. I’ll tranfer it to a mindmap as you suggested, but I was trying to just write it in what it actually shows. The IF is for myself, so I know what conditions I decided upon. The few callings upon the game_controller is because those specifically are going to be important going forward with this game. I also agree with the idea of loading from txt or dat files, I have a basic system setup for saving/loading, I just need to extend that to a general data loader (though that’s probaly just going to be a massive dictionary or array in the config file)

Thanks for your time so far, and any future time spent!

A. It is hard to keep every permutation as a key in a single dict. There is no best way to affect the routes, interactions, and actions based on previous actions and interactions, but here are possible ways:
First, you need an Interaction and/or Action class so you can easily handle each in a single object.

inter1 = Interaction('blah')
inter2 = Interaction('oof')

I recommend using integer levels instead of raw strings for scalability.
-Create multiple dicts, one for each “factor” (level of violence, path taken, whatever), and then your interactions may trigger only if all of the factors intersect for it. A more distributed approach. Some performance benefits.

def get_violence_key(player):
  if player.violence_level > 10:
    return 'high'
  if player.violence_level < 2:
    return 'low'
  return 'med'
violence = {'high': {inter1, inter2}, 'med': {inter1}, 'low': {inter2}}

violence_interactions = violence[get_violence_key(player)]
path_interactions = path[get_path_key(player)]
actual_interactions = violence_interactions.intersection(path_interactions)

-You can instead load each interaction with a condition. Check each interaction when you need to. By far, the slowest and least scalable.

inter1 = Interaction('blah', (lambda plyr: plyr.violence > 2 and plyr.path[0] == 4))

-Similar to 1st option, with traits from the second, define which interactions are used using “factors”, but put the factor definitions for each interaction in the interaction instead of external dict. I think this is the best approach. You directly define where the interaction happens on the interaction. You could even combine opt 1 and this by having a global cache, for some performance gains.

inter1 = Interaction('blah', violence={'high','low'}, path={(0,1),(0,5)})
violence_key = get_violence_key(player)
if inter1.can_do_here(violence_key, path):
  inter1.start()

B. Your code looks better than most I’ve seen. Minor suggestions:
-instead of using a dict with literal strings (prone to error), use an actual class with attributes (look into namedtuples, dataclasses, SimpleNamespace).
-it is best practice to have a if __name__ == '__main__': main() kind of thing. Just do some research
-pull the game loop into a function or method not in main.py if you haven’t already
-use base classes (sparingly) for certain similar classes to avoid repetition
-the way in which functions are run (such as events triggering it, or game loop hardcoded) is extremely important, make sure it’s good and flexible

C. I don’t know much here, but maybe make the monsters super offendable, or make the player have a bad reputation, then dialogue becomes defense or something.
You could have something like violence is extremely convenient for the player because all of the monsters are annoyances (but not aggressive). So if you are a pacifist, you can’t remove these roadblocks, you have to talk to them well or something (harder). A player may choose a mix, obviously.

D. Yeah, that’s fine. I recognized that and I meant condensing it down, but you’d probably just do that in code instead of in plans. You are indenting though, right?

If you implement the third option in A, you can create a very nice loader dictionary, possibly using JSON or similar data formats for easier parsing. (JSON and others should be very compatible with actual python dicts.) You have to figure out what factors exist.
Then, planning it out will be the main challenge, but an official file loading format means you could just plan it in an actual config file maybe (so you don’t have to translate or convert).

4 Likes
  1. That interaction method is legit genius (3A).

  2. Why would I not have my main game loop in main.py?

  3. That’s kinda funny. In C, UT legit ends up using the “They’re in your way” approach for the genocide route. For you to randomly come up with it again lol. I do like the idea of annoying monsters. I finally planned the first monster after reading this. The most dumb but funny monster in UT is Temmie. Which is some weird cat thing? Anyway, introducing SERIOUS TEMMIE. BECAUSE WHY NOT.

  4. Is there any way to keep all this checking efficient then? A massive dict check would prob slow down the game alot wouldn’t it (in replit obviously, but I think in a actual environment too right?)

  1. Make sure you are familiar with set operations and dicts, btw
  2. In programming in general, you want to have your main.py file very clean because it is the entrypoint to your program. Imagine if you didn’t have a main.py file, now you have no game loop. It makes more sense to put it in a Game class or something because it’s part of the game. Doesn’t it seem nice to have a single class that does the entire game?
  3. My idea is that there are two routes (that you can mix), talking to the characters to solve problems and removing the characters to solve problems. I guess often, a character would be a roadblock unintentionally, but not enough to justify killing them morally. Lots of possibilities here.
  4. I know how to design efficient systems well. It seems that perhaps you are not quite familiar with performance in python and big O notation. First, you never want to optimize unless you really have to. Chances are, even with hundreds of interactions to check, even a naive function check is still fast enough to not affect gameplay, even on replit. With a fairly simple caching system and decent code, the performance should be fast for millions of stored interactions (because set intersections and dict lookups are linear and constant time, respectively). If you need more explanation on this, you can ask me.
1 Like

Big O notation? What is that?

Also, it’s true that python can run all of these interactions in milliseconds. I’m more worried about pygame blit functions / pygame transform functions taking forever (tested and depending on file size can make me lose FPS). In the past, I’ve also found it difficult to keep track of dialogue playing in the battle menu/overworld, while keeping consistent animation framerate. Recently I’ve been paying a-lot more attention to the neatness/efficiency of my code, and I came up with a sort of plan, which is my game_controller class, where I call clock.tick(fps) once a loop, then keep track of player, enemies, every active sprite in one big dictionary. But when it comes time to code it to be functional, idk why, but I just hit a procrastination blank.

I also forgot to mention I’ve never used JSON before, so I don’t get why that would be more efficient than a simple txt or py file.

While I remember, how did you learn python/other programming languages. I would guess that you’re at least in college based on your writing style and general knowledge of code. Did you just use docs as needed and teach yourself (what I’ve done so far), or is there some really good program/school for this sort of thing?

1 Like

Replit has their own Python course, 100 Days of Code.

2 Likes

Big O Notation is, in short, the time it takes for an algorithm or operation to run, but very generalized: it only tells you how quickly the time grows with more “stuff”. Example: O(n) is linear, like iterating over a list. O(1) is constant like list indexing. O(n*log(n)) is the performance of the builtin sorting algorithm. Dicts and sets have a lot of constant time operations.

I do have a lot of experience trying to get performance from pygame. Here are some tips:
-Avoid unnecessary transforms in a frame, they are expensive. For example, cache a rotated image (do not replace though). Use the cheap, fast transforms, not the expensive smooth ones.
-Do not use per-pixel alpha, (every pixel in a surface has a transparency value). Very slow. I think PNG surfaces might be like this. Instead, use colorkeys or whole-surface transparency. Use the .convert() method to make sure the surface format aligns with the Display.
-Are you actually utilizing pygame sprite Groups? (Not about performance). You are inheriting from pygame Sprite, but it’s not completely clear to me why.
-In GameController.blit_all, use fblits instead of blits according to the docs. Also, no need to convert to tuple beforehand.
-Profile your code (measure what functions take the most time). An easy profiler is cProfile.

Pastable example:
if __name__ == '__main__':
  if __debug__:  # will not run if program is run in optimized mode
    import cProfile
    import pstats
    profiler = cProfile.Profile()
    profiler.enable()
    try:
      main()
    finally:
      profiler.disable()
      stats = pstats.Stats(profiler).sort_stats('cumtime')  # sort by cumtime
      stats.print_stats(25)  # number of functions printed
  else:
    main()
# tottime is time in the actual code of the function,
# cumtime includes the function calls that the function makes,
# percall time is provided for both

-Most of the time should be spent in blits, the clock tick, and maybe the display update. Look at the tottime for the functions that are directly taking some time to run.
-Sometimes, performance can be outside of your control. The blits usually cannot be optimized much, so blitting a lot of large images might still cause a slowdown. Keep in mind that sometimes replit is just slow randomly, and you won’t get above 40 fps sometimes.

So, most of your code looks all good and expected. Looks like there isn’t a comprehensive animation system yet. But there’s quite a lot done actually. Focus on the important systems, the rest can be implemented/refactored better if you really need it. In my experience, thinking out a system can take a while but also is very nice for the future.

The reason JSON is good is because you can directly load it into a dict from the file, without any parsing hassles or syntax worries. JSON is also widely supported. If you parse your own text file, you have to create a loader and your own syntax.

I first learned basic javascript, in code.org. Nothing much happened out of this. I could argue that my programming journey started even earlier, in games and small systems very similar to programming (such as Minecraft redstone and commands). Then, I picked up python in replit quite recently. This is really where the stuff starts. Python crash course, then pygame game tutorial from Tech with Tim (with my own twist so as to not copy). Then, I tried (and kind of failed) to make more games. But the key was that I didn’t just follow a specific tutorial, and I researched everything that I needed but was unknown to me. (I also learned basic C# .NET from a class at this time.) So yeah, self-taught using docs. I have yet to try a good course, I generally disliked the formal courses. So, I am the start of my programming journey, and am hoping to develop my game into something playable eventually.
Pygame and pure python is my niche basically, I don’t know much about other languages or libraries.
YT videos can really give a lot of motivation, either game design, indie development, or actual pygame. (You may want to check out the channel DaFluffyPotato).

1 Like

Useless Information: The mind map is becoming ridiculously massive. It took nearly 2 hours worth of writing/re-writing to get a good draft of the Intro, Titlescreen, Savescreen, Room 1 (Out of several hundred minimum), and the Geno run of the first battle. It takes up like 7-8 monitor screens of space. (This is for just 1 specific one, where no past runs have been completed)

Anyway, I intend to add functionality for pygame.sprite.Groups later on, for collision purposes (you can see how in battle it’s useful to check every projectile for contact at once).

I wonder, with the per-pixel alpha, I believe I won’t be using any transparent images. If I call .convert() on every image at the beginning, will transforming the image keep it in that format? Idk how I’d use colorkeys and/or whole-surface transparency.

I’m also wondering how to efficiently do projectiles. If projectiles rotate, then they’d have to update the image each frame. But it seems like it would take a massive amount of working memory to have 360 images for each attack, with possibly hundreds of attacks in the final game. Even if I cache it each time it’s called to a new rotation value, this only becomes useful for common enemies, as bosses are unique.

Thanks for sharing your journey of code so far with me! I’m going to try my first formal course next school year, so that should be interesting. “…hoping to develop my game into something playable eventually.” What game are you making (if you feel like talking about it)?

How many projectiles are there and how big are they? Also, pygame cannot rotate rectangles (for collisions), but this shouldn’t be too problematic.
If you are rotating images, you probably will be using transparency because the corners of a 45 degree image have to be transparent. Use colorkey. A colorkey is a designated color that is considered transparent for that surface.
You may also want to experiment with special flags for blitting such as adding the colors instead of overwriting, for nicer looking effects.
Rotating each projectile image per frame is obviously not feasible. Maybe for individual enemies, but not really for a bullet hell. Two good options:
-Round angles to a specific interval, such as 360 angles or 60 angles, for caching. (If the projectiles are sufficiently small, the rotation transforms will look very similar.) You only have to hold them for one battle or even one frame.
-Have your own predrawn, rotated projectile sprites. May look much nicer but obviously less angles to work with.

Having many projectiles, you’ll have to update their velocity and angle every frame too, and check collisions. This can be optimized a lot by using a third party physics library, but obviously this is only if it’s really, really slow. For rendering, if the projectiles are small enough (less memory) then caching is needed.
Only use convert() once, right when you create an image using pygame.image.load() because the pixel format may be different. By default, surfaces instantiate to the display format, so other surfaces should be fine.

So, my game, I’ve actually had 3 main game ideas during my time in replit. I am not sharing the specifics, but all three are physics-based, skill-based games. Currently, none of the games have been developed enough to be really playable (except maybe one), all prototypes, and I’ve refactored one of the games at least 3 times. I realize about myself that I am very slow at actually programming stuff (perhaps because of perfectionism). I’ve created a very nice framework for my games, but it took like a year so, maybe it was worth it or not. Motivation for continuing came from yt videos and ask replit forums.

1 Like

A: Makes sense, and projectiles will vary sizes, from maybe 7x7 pixels to 60x60 pixels to 60 x 12 pixels. Is there a way that rendered images aren’t stored in immediate memory? Then they only are stored while called.

B: There can be up to 150 or so projectiles at once on the screen (average 20-40). What I’m thinking rn is some sort of dictionary, that contains instructions to:
{move from a to b over 56 ticks}
And the animation is stored seperately:
{“costumes”: [costume 1, costume 2, etc.], “hold_time”: [50 ticks, 100 ticks, 20 ticks]}

C: That’s awesome about your games. I feel like every good programmer has to want to be a perfectionist. We learn by rewriting code over and over and thinking about code over and over (a long run-on sentence)… The best software developers I’ve met or seen have all been at least near perfectionists. When we push for perfection, we improve. (I have absolutely no idea why I decided to write this little speech lol)

A. So, there are really only two options.
-Load all images at the start of the program and hold them forever
-Load images for the duration of a battle, then unload them when not needed. Make some sort of resource manager class. It should be relatively simple to implement.

B. That is indeed many projectiles (for maximum scenario). You will definitely have to make a fast cache or something, and optimize well. You’ll probably use fblits(). You have the right idea with the animation (though I don’t really know why it’s in a dict instead of an attribute). I would recommend a constantly increasing “animation_cycle” number, costume for the first 50, then the next 40, next 100, etc. Subtract the total from animation_cycle when it gets too big. Implement some settings for interrupts and changes.
One large problem I see is that you might be implementing time wrong. Say you switch to 30 FPS, everything should still look the same. Make sure this is the case.

C. The thing is, I still want something functional but I am making stuff too slow. The most developed game I have was almost just a freestyle, code is kind of unintuitive. If you spend too much time on the details, you’ll never get anything done, but if you freestyle, you are hampering yourself for the future. But I agree that we improve much more this way.

If you need help implementing any system, I can help you with it.

1 Like

For the time, I’m not quite sure how to use it efficiently. I originally tried to use a delta time setup, with something like: player_speed * delta_time/fps, but it didn’t really work correctly.

I tend to use dicts in general, due to that I’ll need a unknown number of counters, and it’s nearly a pet peeve to see so many variables meant for similar purposes written out as a list.

The delta time setup is almost correct, but not quite. Either multiply by delta_time, or divide by fps, not both. (I’d recommend delta time.) Multiply any relevant time-based values by delta_time.

player.position += player.velocity * delta_time

So, you want to avoid something like this?

player.animation = [[costume 1, costume 2, etc.], [50 ticks, 100 ticks, 20 ticks]]
player.animation[0][1]

That’s not really what I meant. Translate the dict directly into attributes:

player.costumes = [costume 1, costume 2, etc.]
player.hold_time = [50 ticks, 100 ticks, 20 ticks]
player.costumes[1]
# player.animation["costumes"][1]

(You may want to ‘zip’ them together into one, it’s more intuitive.)
You can also use namedtuples or dataclasses or SimpleNamespace to directly replace the dict with an analogous object.

3 Likes

How do you zip them? :sweat_smile:

If you have at least 2 lists, you can ‘zip’ them, example:

a = [1,2,3]
b = ['a','b','c']
print(list(zip(a, b)))
# [(1, 'a'), (2, 'b'), (3, 'c')]

I think it makes more sense to put your costume right next to it’s hold time rather than having them connected only by an index.

player.animation = [(costume 1, 50 ticks), ...]
2 Likes

Alright, I’ve done some work on the actual code (as well as the mindmap).
I ended up defining the Interaction class for now in the controller file.
Does something like this look right for a interaction parameter defined in the config file?

savepoint_dialogue = {"Room_1": [["Monster Name", "D" (Condition, if monster dead in this case), "Dialogue"], ["Monster Name", "A" (Condition, if monster alive in this case), "Dialogue"]]}