Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Yal

663
Posts
11
Topics
1,583
Followers
25
Following
A member registered Aug 12, 2015 · View creator page →

Creator of

Recent community posts

Glad it wasn't my fault! :P

If it becomes an issue in the future, the number 1 way to improve performance would be to have two different screen size constants, so the mask surfaces (the things that define how the view is sliced up) stay small, and each mask pixel is used by multiple screen pixels. You're gonna need to scale up/down coordinates in a couple places but it shouldn't be too hard to figure out, hiding the jagged edges on the screen (due to the larger pixels in the mask) might be a bit trickier though.

(3 edits)

I have the Linux export license for Game Maker so I could handle that myself, I just didn't realize there was a demand for it - this game gets an average of less than 10 downloads per year.

(Also Christmas season is over already so it feels a bit late bringing this up NOW, lol - I guess that means I have about 12 months to do this though)

Also note that this game requires a mouse, the device shown off in your previous posts looks like a GBA SP without a touchscreen or a second stick so I don't think Santamaku would even be properly playable.

I'm probably drawing more influences from Cruelty Squad than Hotline Miami (other than the art style) lol, but glad to hear there's other (canon!) stuff that tries a similar mix. If I ever did something truly original I would probably have a mental breakdown

(1 edit)

Each WMHM can only have one texture atlas for the autogenerated terrain and each chunk/level can only have one texture atlas for its terrain, but they can be different. There's no upper limit to how big an atlas can be either (but you need to use the atlas editor to generate the texture / footstep-material metadata file for the size to be recognized by the level editor)

Also note that you need to set the atlas size twice for the atlas used for a WMHM, once for the texture data layout (regular atlas editor) and another for how to group it into biomes (metadata in wmhm script)

For chunk size it's the globaldata script, constant WMHM_TERRAIN_CHUNK_SIZE. The actual size of a WMHM is set up when you define it (wmhm script), the example one is 32000x32000x4000 (this affects the scaling factors used when reading the heightmap used for generation)

I have no plans on adding in third-party code (for both legal reasons, and my ability to provide technical support)

(3 edits)

-12341 is the value of the NONE constant (one of the reasons I picked that number was to make it instantly recognizable). You need to set global.load_room to the current room before changing to the evolution room (and also the global.load_x, load_y and load_direction variables to the player's current x / y / drawdir respectively) otherwise it doesn't know where to put you back - and that's exactly what's happening right now.

(For an example of how to do this, check out player_step_fill which handles the "steps in tall grass to the next random battle" counter, and which sets these before changing to the battle room if an encounter is triggered - side note, this function would also be where you put things like "monsters in daycare gets EXP" and "countdown to eggs hatching" happening)

Yeah, just as you imply I pretty much stole the system from soulslikes :P (stamina bar included, heh)

A lot of older scrolling beat 'em ups have basically no mechanics to speak of, you do the same handful of actions all the time (you'd think with 6 total face/shoulder buttons on a SNES controller there'd be more than basic attacks, jumps and the "hurt yourself for dubious benefit" special move button so many of them stuck with) so I felt like I had to expand on it somehow... also I suck at character animation so I wanted to keep the number of attack animations low; the single-frame shield/dash poses were perfect for this.

I feel like soulslikes are basically a combination of scrolling beat-em-ups and metroidvanias so it'd be interesting seeing if more indies realize the potential of backporting mechanics going forward - we've got the River City Ransom reboot and one of the UFO 50 games both adding a couple modest RPG elements to a scrolling beat 'em up, far from a big trend but it's something.

It is made in Game Maker, you can tell from the file format.

(no guarantees UTModTool works with it though, if that's what you're thinking - I'm using the latest stable GMS2 with some YellowAfterlife extensions on top)

I'm trying my best to make every line of dialogue worth reading :P

Thank you! ^__^

(1 edit)

Oh you got the Hollow Knight reference, nice 🍔

why do you think the first area is named bread ruins lmao

Oh yeah, I forgot to post about it here, but a fix for this issue is up in the latest demo! (The code that reads the inputs would always use the gamepad for player movement if one was plugged in and it just never ocurred to me to test having a gamepad plugged in and NOT use it)

Glad you like it! ^__^ This started out as just a silly little jam project but now I'm so invested in it I wanna make it my magnum opus, including dusting off ALL my old ideas (hence why some really obscure stuff like Shiny Wings and the Crossclaw Shield is back).

I'm legit starting to lose track of what can and cannot be gotten so I might have to give up and just alter the max values to be the true ones soon (instead of taking into account what is currently unreachable - like the shard in Chessmaster's Tomb under the fake burger block for instance). 

In the original demo I knew that tally exactly but between new mobility options, me adding tons of currently unreachable goodies, and a bug that let you collect certain carrotcoins more than once I'm starting to suspect my notes have gotten desynced, and there's no good way to double-check everything...

You know what they say, there can never be too many 🍔

I looked into this and I think I figured out what's happening - you have a gamepad plugged in and Analog Input turned on, but you're using the keyboard to play? For some reason this makes directional inputs always read as zero so there's probably a priority issue (so the logic uses the blank inputs from the gamepad even though it knows you're in keyboard mode).

You can solve this by opening the options menu and turning off Analog Inputs (or unplugging the gamepad), I'll try to get a fix for this done ASAP.

If you can't move at all, I hope you like decor in the first room! I worked really hard on it :3

Add a value between -1.0 and +1.0 to the final calculation of the "tlc_" variables (="Top Left Corner").

For the X value negative values moves the view left (so the object appears further to the right in the view) so I think that's what you want to do, for the Y value negative values moves the view up (so the objects appears further down).

I'm not sure what will look right so you might need to try different values, but I think -0.35 for the X coordinate and +0.15 for the Y coordinate will be a good starting point with the screen size you're using.

I'm going for like a 50-50 mix of Pizza Tower and Undertale vibes so there's story stuff happening in-between going fast, but fluid movement and going fast always was a priority.

Some tips:

  • You can disable dialogue in the Speedrunning options if you really want NO story
  • Always hold the run button (it increases your jump height even when standing still)
  • You can stack the recoil from the basic spell to go over the regular speed limit both vertically and horizontally
  • On levels with more than one Crystal Burger you can collect more than one in the same run to Double Flip, this is basically the "Lap 2" mechanic

Here's the official GameMaker tutorial series on the code editor. (I recommend using GML Code since Visual doesn't really make things easier, just prettier to look at.)

Yes; and I'm saying that...

Is it easier to explain as bullet points?

  • the view is currently always centered around the player
  • however, you always cut out the part in the top right (= not the center)
  • therefore, you need to shift the view towards the top right, so that the region you cut out will contain the center (where the player is)

Player 2 is in the center of the screen here so it seems to work except you should shift the view manually (e.g. add 0.25 to the x coordinate and -0.125 to the y coordinate) to compensate for the point of interest always being in the top right corner?

No no no, you'd comment out the first area (we don't need batches)


and then only change the upper bound of the loop (only loop over first player)


(1 edit)

OK, I think I get what's happening now - since you assume manual control of the view the batching system gets in the way. I fiddled around with it and got better results after the following changes:

  • comment out everything starting at the "for(var c = 0; c < voron_OBJECTS_MAX - 1; c++)" line and ending right before the "//If not in a batch, just target actual position." line
  • A little further down change the max value of the "//Compute screen positions using relative orientation" loop to 1 instead of the number of players

Now there's no batching, and the average position (used to center the main view) only takes player 1's position into account. The only drawback is that the pop-in assumes player 2 is in the center of the entire screen, but you can solve that by just pushing the view around with some constant factors based on screen size.

What changes did you add more exactly? Only removing the clamp or also hardcoding the center position + adding the new adjustment factors?

I think the reason the P2 camera isn't centered is because the view isn't allowed to go outside the level and it's always 1 screen big, so when forcing it to focus on a given position without taking the relative positions into account you can end up with a suboptimal view. It's easy to tweak at least, it's the lines near the bottom of obj_viewcontrol's End Step event.

global.voron_tlc_x[c] = clamp(global.voron_worldpos_x[c] - VIEW_W*xf,0,room_width  - VIEW_W)
global.voron_tlc_y[c] = clamp(global.voron_worldpos_y[c] - VIEW_H*yf,0,room_height - VIEW_H)

Just removing the clamping to let the view poke outside the room might help (though we don't wanna do that for the main view following the player)

if(c > 0){
   global.voron_tlc_x[c] = global.voron_worldpos_x[c] - VIEW_W*xf
   global.voron_tlc_y[c] = global.voron_worldpos_y[c] - VIEW_H*yf
}
else{
   global.voron_tlc_x[c] = clamp(global.voron_worldpos_x[c] - VIEW_W*xf,0,room_width  - VIEW_W)
   global.voron_tlc_y[c] = clamp(global.voron_worldpos_y[c] - VIEW_H*yf,0,room_height - VIEW_H)
}

You might need to add a factor adjusting the view position based on where you have the pop-in (since the popin isn't centered in the view, the view should be offset to make sure the bit you cut out is actually centered) but I'm unsure what direction and magnitude will be correct here so I'd just recommend you to play around with the numbers until you get it right - but my guess is subtracting VIEW_W*0.425 from the x coordinate and VIEW_H*0.075 from the y coordinate (half of the 0.85, 0.15 coordinates we use for the center).


To import this to an existing project it should be enough to import obj_viewcontrol, sh_voronoimerge, and the voron_init, voron_player_get_id scripts (either right click empty space in the asset browser and pick Add Existing, or open the stuff in an external editor and copypaste the code into fresh objects created in your new project - I prefer the latter method for more control but the former is probably the most convenient), the init script has a pragma that makes it automatically be ran when the game loads so you don't need to manually set it up.

Next have an object named "parent_player" (which your player objects and goal objects inherits from), and give your player a variable player_id which is 0 for player 1 while the goal also has the same variable but it's 1 (this is what the control object uses to figure out where to put the views).

Then just place an obj_viewcontrol in the room (or better yet, have the player create one in its Room Start / Create event so you don't need to manually place one in every level) and it should hopefully handle everything on its own from there.

(1 edit)

Oh right, multiplying by a smaller value makes distances shorter than they actually are - my bad, the 9/16 factor should probably go for the y value instead of the x value.

To make the outline thicker just increase the "0.0001" used for that comparison, 0.001 is probably too thick so maybe 0.0005 will look better? (If you have a specific pixel width you want, just divide that with the screen width to get the value to use here)

As you can see for both of these, I don't really know everything and a lot of this is trial and error :P You kinda can't get away from it with shaders, they're very visual compared to regular game logic code...


The "player 2 position" used in the shader is set in the control object's Post Draw event, it's the third and fourth row of the pos array and they're percentages (since they're coordinates in screen space). So for instance if you always want the pop-in in the top right corner, you could change it to something like this:

var pos = [
     global.voron_screenpos_x[0],
     global.voron_screenpos_y[0],
     0.85,
     0.15,
     global.voron_screenpos_x[2],
     global.voron_screenpos_y[2],
     global.voron_screenpos_x[3],
     global.voron_screenpos_y[3]
];

Your first guess is correct, the view is still centered around the 2nd player's relative position, but you can just explicitly change that (since you're not going to actually have a second player) to get the cut-in wherever you want.

The black outline is pretty simple, check if the distance is "close to the max value" (e.g. < 0.001 difference, change to a larger/smaller value to change thickness) and if so, set the gl_FragColor to black rather than any of the sampled colors.

Despite the simple idea it's a bit of a mess but I'm thinking two changes will be enough:

#1, the distance check

if(dist < vsize[c]){
      bestdist = dist;
      bestid = c;
      if(dist > vsize[c] - 0.0001){ //This part is new
           bestid = 9;
      }
}

#2, at the end

if(bestid < 9){
    gl_FragColor = sampcol[bestid];
}
else{
    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}

Making it circular gets more complicated but I think you can "cheat" a bit to simplify this - instead of taking the distance between the actual pixels when doing the distance check, make two new vec2 variables which are copies of the player position / current fragment position but you multiply the x coordinate by 9.0/16.0 (the inverse of the aspect ratio).

So something like this might be enough, just replacing the dist check:

dist = distance(screenpos[c].xy*vec2(9.0/16.0, 1.0),v_vTexcoord*vec2(9.0/16.0, 1.0));

Got a minimal example working with these changes:

Control Create event at the bottom:

u_voron_vsize = shader_get_uniform(        sh_voronoimerger,    "vsize"            )
p2size = 0

(+ added some keyboard events to let me increment/decrement p2size with 0.001 at a time - it's in screenspace coordinates so 1.0 means "100% of the screen width/height" so in-game you want small decimal values for a small pop-in view)


Control Post Draw event, line 14:

shader_set_uniform_f(        u_voron_vsize,    0,    p2size,    0,    0)

(note how vsize[0] doesn't matter so I always set it to 0, but player 3 and 4 are set to 0 because we wanna disable their views - so different reasons)


sh_voronoimerger (Fragment):

New line before main:

uniform vec4 vsize;

Lines 20~31 changed like so,

     //Go find the nearest screen position and use that
     float bestdist = 999999.99, dist;
     int bestid = 0;
     for(int c = 3; c > 0; c--){
         if(c < num_players){
             dist = distance(screenpos[c].xy,v_vTexcoord);
             if(dist < vsize[c]){
                 bestdist = dist;
                 bestid = c;
             }
        }
     }
     gl_FragColor = sampcol[bestid];

I.e. run the loop backwards, don't run it for 0, but default to 0 if you don't pass the distance check any time.

No, but there's a PDF manual "ME64_OpenWorld_UserGuide" which explains how most of the systems work. It's a bit brief but it covers all the nitty-gritty details like how levels are mapped to the WMHM coordinates and how to use the atlas editor to set up coordinates + footstep sounds.

(1 edit)

OK, for cases like that the second option probably will work - basically manipulating how the "distance" is computed in the shader so you can overlay the views however you want. 

If you want the additional views to take precedence no matter what, it gets a bit more complicated, but I'm thinking something like this:

  • Add additional uniforms for "view size" parameters for all views (these could be vec2 if you want different aspect ratio horizontally or vertically or a single vec4 for them all if you want uniform size)
  • Rather than checking all positions to see which one is nearest, the loop checks the popins only (view 1-3) and if it's closer to the focal point than "view size", it uses that texture.
  • If it's NOT close enough to any focal point, it always uses the view 0 texture.

So basically you don't pick the nearest view, you check if you're in one of the popin views and just want a yes/no answer.

(Additionally, you can fade in/out the views by changing the view size that gets passed into the shader, and set it to 0 to disable the popin view entirely - and of course modifying the focal point moves the view around the screen as well, like before)

I don't understand your question, could you please elaborate?

If you mean turning the effect on/off: yes, obj_viewcontrol takes control over the application surface when it is created and returns it when it's destroyed / the room ends, so you should be able to just create one when you want the effect to happen and remove it when you don't want it anymore.

If you mean controlling the effect so the alternate view slowly slides in from outside the screen: I think the easiest way to do this would be adding a new uniform vec4 to the fragment shader which contains a "distance bias" for each of the 4 players. Subtract the corresponding value when calculating which player is nearest. This means if you set distance_bias[0] to (size of your view) and then gradually decrease it, player 1 will have the entire screen space to themselves because their view "bleeds over" the others, but as you decrease it the other views fade in.

(1 edit)

Just to make sure I test-ran the GMS2.3 version in IDE v2024.8.0.169 / runtime v2024.8.0.216 (latest version) and everything seems to be working fine, so there's currently no need to update it.

I'll go ahead and hide the GMS2.2 version since nobody uses that anymore, hopefully that helps avoid some confusion.

Congratulations! This is probably the hardest game I've ever made, I've not even beaten a complete run myself.

Since there's only one boss in the demo Fielder might as well be the champion. 😉

I had plans for a sequel like that but I burned out on making Touhou fangames after doing only those for like 2 years... so right now it's postponed while I work on my own IPs for a bit. (My main project Burgertale is basically a kitchen sink of every metroidvania I've ever made so hopefully you'll enjoy it when it's done - I can't legally put Touhou characters in it but I have plans to sneak in a few subtle references ;P)

Total is the actual number of stat points, it gets subdivided proportionally over the stats (so the mhp-atk-def-mag-res-spd values are relative - e.g. if ATK is twice as high as DEF, the final stat will also be twice as high, but the actual value that gets stored in the end is TOTAL x ATK/(sum of all 6 stats), not ATK as-is).

The reason for why it's this convoluted is so it's easy to balance monsters - if two monsters has the same stat total, they should theoretically have the same power. (You can see this in Pokémon where there's tiers like "pseudolegendary" (600 stat total), the games never draw attention to this but competitive play communities does and that's where I got the idea originally)

  • The only keys used are Z, X, enter, shift and the arrows.
  • There's no map.
  • There's no unlockable swimming, but there's equipment that lets you move unhindered underwater.
  • The ending should be on the critical path and be unmissable, just be prepared that the story ends kinda abruptly and without proper payoff to some of the plot points.

Thank you! ^__^

Hopefully it's even better when it's finished, there's a lot of important features left to do that I haven't had time for yet (like the storage system, scarf upgrades, a blacksmith that can repair your weapons etc)