34 comments

  • em-bee 12 hours ago ago

    nice, but i find it very hard to play. acceleration is either not enough and you are pulled in by the planet, or it is to much and you are getting away so fast that you can't counter steer. shouldn't gravity take care of that? if i am in orbit, then speeding up along my trajectory should slowly increase the orbit, and slowing down should decrease it. but speeding up takes me immediately out of the orbit as if the planet had no gravity.

    • mvx64 8 hours ago ago

      Thanks for trying it out. It's a regular inverse square law, no tricks. The numbers (masses, distance) determine the final acceleration but not the actual trend of the curve.

      I've become too familiar with it over testing to notice unintuitive behaviour, but I think I understand what feels off: in real world units, the gradual region you describe is very wide, and feels linear. This would make for very boring gameplay (imagine spending minutes to reach the planet). You need to keep the playable area [radiusForce0, radiusForceMax] small. So you will either map that small [r0, rmax] into real world [F0, Fmax], which means the force will be almost constant across, or "compress" the [F0, Fmax] curve so that you can fit both [zero outer space gravity, strong surface gravity] into that [r0, rmax].

      That's what happens here, I probably tweaked the values for the second case. It's kind of an accelerated version of reality and the margins feel very tight, and you have to "buy into" that reality.

      For example, Master difficulty in Mission 1 may seem impossible, but if you try to be gentle and find a balanced orbit, you can complete it with miniscule fluctuations in distance and minimal input.

      Just rambling though, I never really actually designed or balanced the game.

      • em-bee 6 hours ago ago

        maybe some kind of indicator to show which level of acceleration/speed is best would help.

        the unintuitive behavior is that it is very difficult to find that balance. if i am to slow i crash into the planet, if i am to fast i leave orbit with no chance to get back in time. being gentle always results in being to slow. in other words there is no gentle way to reach the balance. and if i don't know where that balance is, i don't know what to aim for.

        you may argue that finding that balance is the goal of the game, but then i guess the game is not for me. i lose interest if i have to try 10 times and still can't figure out how to do it right.

        • mvx64 6 hours ago ago

          Understandable. That's kind of the goal, the gameplay is very shallow complexity-wise, so raw responses/difficulty is one way to put some playing time into it.

          There are some methods that help a lot, like keeping a completely perpendicular or parallel viewing direction, and adjusting the distance with the corresponding set of thrusters. Even slight angles mean you have to randomly mash forward/backward/left/right and hope to keep a steady orbit, it's not gonna happen.

          For what it's worth, even for me now, it would take more than 10 tries probably to beat Master on Mission 1.

  • ioma8 21 hours ago ago

    Didnt you think of making it open-source on github?

    • mvx64 20 hours ago ago

      The game-side code, not really (I am ashamed of reading it myself). The engine-side is a bit less shameful, but I'd rather not as I mentioned elsewhere, especially on github. I may change my mind though.

      • Krei-se 16 hours ago ago

        That's a shame, i like how you go about using Rust to have crystal clear structure while sidestepping the unreadable side that comes from using all of its nuances.

        I'd kinda like a look. My Code is dependency free where possible (up to the graphics library for making the pixels go on screen) too - and everything else is a clean pipeline. Soooo i can only speak for myself, but if you have some parts that don't make you cringe much but proud - please share!

        It's less about implementation of functionality, im mostly curious about your style! :)

        • mvx64 15 hours ago ago

          I'll see if I can produce a reasonably readable stripped down version (the lib + an example scene) and I might be back soon ;)

  • armitage__ a day ago ago

    I love custom game engines. Looks fantastic! Will you be sharing the source?

    • mvx64 a day ago ago

      Thanks! For several reasons, most probably and regrettably no, for now at least.

      More than happy to talk about any specific part however (e.g. how scenes are handled, the code itself, or how particular features are implemented or optimized).

      • nextaccountic 15 hours ago ago

        You talked about using Rust as a better C so I just wanted to ask, do you define any enums with payloads? (also called "sum types" or "tagged unions" in other languages) (edit: also called "algebraic tyeps" and there's an article about it in the front page, though this is a slight misnomer)

        Things like

            enum Something {
                One(String),
                Two(i32),
            }
        
        Also, how is your usage of Option? (one such enum)

        I think this plus pattern matching is the foundation of Rust's superpowers. It's also very old tech and absolutely not Rust's invention, present in languages like OCaml and SML. Hence the early Rust slogan, "technology from the past, come to save the future from itself"

        • mvx64 15 hours ago ago

          Actually yes! I use it when passing a texture into a draw function. I have a TexOrColor enum, and when calling the function you either provide an &Image or a &Color. Before that, if I wanted a colored textureless model, I passed a dummy 1x1 texture to sample from.

          And of course, Options and pattern matching are easily the best part of the language and very powerful. I am obsessed with things like "let x = if {...}".

      • galangalalgol 20 hours ago ago

        Could you talk more about the subset of rust you settled on?

        You said you didn't explicitly use simd, but did you do anything to help the optimizer autovectorize like float chunking

        • mvx64 19 hours ago ago

          It's a very procedural style. I have not used: iterators, lifetimes, Arcs/Boxes/RefCells and whatnot, any kind of generics, data structures other than vecs/arrays, async, and many more. Also avoided functional style, builder patterns...

          I only used traits to more easily implement the scenes; a Scene needs to implement a new(), a start() and an update(), so that I can put them in an array and call them like scenes[current_scene_idx].update() from the main loop.

          Also, I used some short and simple closures to avoid repeating the same code in many places (like a scope-local write() closure for the menus that wraps drawtext() with some default parameters).

          The vast majority of the time is spent in the triangle filling code, where probably some autovectorization is going on when mixing colors. I tried some SIMD there on x86 and didn't see visible improvements.

          Apart from obvious and low-hanging fruit (keeping structs simple, keeping the cache happy, don't pass data around needlessly) I didn't do anything interesting. And TBH profiling it shows a lot of cache misses, but I didn't bother further.

          • nextaccountic 15 hours ago ago

            > I have not used: iterators,

            Here's a counterpoint: every time you write a for loop in Rust, you are using iterators.

            • mvx64 15 hours ago ago

              You mean implicitly? I am aware that idiomatic Rust strongly prefers iterators over indices for performance, but in my case, the only place where it really matters is when counting pixels to draw, and there is no kind of collection there, just x,y numbers.

              • nextaccountic 15 hours ago ago

                Yep implicitly, for receives an IntoIterator, so it iterates either on an iterator like 0..10 or something that can be converted into an iterator like &myvec

                (Note that it was a severe design flaw to make ranges like 0..10 iterators directly rather than just IntoIterator, because this means ranges can't be Copy and as such it's inconvenient to pass them around.. but fortunately they are going to fix that in a new edition)

                But actually..

                Do you mean you prefer writing for i in 0..myvec.len() and then accessing myvec[i], rather than using for x in &myvec or for x in &mut myvec, and using the x directly? But why?

                • mvx64 8 hours ago ago

                  Interesting, I will look more into this.

                  To be honest, grepping the source, I found a couple of places with for x in &myvec. Probably tried them while learning the language, they worked and left them there.

                  But really, I am just more used to the old way. It visually feels more familiar, I have an explicit i to do things, and it's easier to delete/manipulate elements while in the loop and not invalidate anything. It's not that I am against an iterator if it matters and it's better, of course.

                  In my case, the important loops are like "for i in x0..x1 { draw(i, y); }". Is there a way to turn this into something that, for example, skips the bound checks or optimizes it in any other way?

          • galangalalgol 18 hours ago ago

            Without lifetimes arcs boxes or refcells do you have a lot of clones? Or a lot of unsafe? Or is it mostly single threaded?

            • mvx64 17 hours ago ago

              It's all single threaded. Just structs being passed around. For example, the mesh drawing call is:

              drawmeshindexed(m: &Mesh, mat: &Mat4x4, tex: &Image, uv_off: &TexCoord, li: &LightingInfo, cam: &Camera, buf: &mut Framebuffer)

              so there is also no global state/objects. All state is passed down into the functions.

              There were some cases that RefCells came in handy (like having an array of references of all models in the scene) and lifetimes were suggested by the compiler at some other similar functions, by I ended up not using that specific code. To be clear, I have nothing against those (on the contrary), it just happened that I didn't need them.

              One small exception: I have a Vec of Boxes for the scenes, as SceneCommon is an interface and you can't just have an array of it, obviously.

              • galangalalgol 17 hours ago ago

                Thanks! That seems like a nice subset for a lot of use cases. You say it isn't functional, which in rust it is hard to be pure, but if you consider it a spectrum, the style you describe is closer than most game code I've seen.

                • mvx64 16 hours ago ago

                  Right, it's a spectrum, you can't avoid some things (and rightly so).

                  Another soft rule: no member functions (except for the Scenes); structs are only data, all functions are free functions.

                  Also no operator overloading, so yes, lots of Vec3::add(&v1, &v2). I was hesitant at first but this makes for more transparent ops (* is dot or cross?) and does not hide the complexity.

                  The whole thing is around 6-7kloc and I think it would be possible to rewrite in C++ in a day or two.

                  • galangalalgol 14 hours ago ago

                    I don't find the idea of rewriting the subset of a blas library I want to use for each project very fun, but I bet with so few dependencies a stripped binary gets pretty small.

  • creikey a day ago ago

    Very cool project, can't play because on mac but looks like cool approach

    • mvx64 a day ago ago

      I have considered getting a Mac in the past (even a very old Air) to make and, most importantly test, Mac builds, but I never got to it and cross-compiling to Mac seems like a pain, if at all possible. If you have a VM laying around though, it should work.

      A good thing about the approach is that if it compiles, it should work exactly the same everywhere and with predictable/linear performance, no matter the environment or driver situation.

      • p0w3n3d 21 hours ago ago

        I believe you can build to mac targets on github though

        • mvx64 20 hours ago ago

          Didn't know this, but I'd rather not use github to be honest. Isn't there also some kind of signing required to publish a Mac build?

          • Klonoar 8 hours ago ago

            You don’t need to sign it, the user just has to do some leg work to run it without it.

          • p0w3n3d 19 hours ago ago

            you're right, I wasn't aware of it. Of course you could build it and leave it as is, so we would need to accept the binary, but still - a lot of work. Makes some good projects made by devoted programmers exclusive to other platforms, sadly.

    • ioma8 21 hours ago ago

      It works perfectly cool through wine. Also i think adding mac builds wouldnt be hard given its simple dependencies.

      • mvx64 20 hours ago ago

        Yeah it should be just a simple cargo build. But for the same reason it should be playable through any emulator/translation/VM layer as you mentioned.

  • bmn__ 14 hours ago ago

    > I do my own physics, quaternion/matrix/vector math, TGA and OBJ loading.

    > FPS ranges from […] [70-80] on a 2005 Pentium laptop

    > I am using [Rust] more as a "C

    Great minds think alike! Please, I invite you to submit your work for approval here:

    https://xcancel.com/tsoding/status/1960511663788188095

    https://xcancel.com/tsoding/status/1964636951358894337

    • mvx64 7 hours ago ago

      Nice discussions! I've seen many of these projects lately around related communities. Happy to see a trend around this, as I really think there is actual merit in the topic. My project was more of a random timesink than something that pushes the matter forward though.

      Do you mean directly e-mail/contact said person?