Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(+2)

An innovative top-down shooter where your primary tool alters the environment.

Pros: simple and easy to understand, score-based rather than ending-based (great for jams!), one of the most creative uses of the theme I've seen. I love the fact that the chains actually alter the environment forcing you to plot out which order you trap the enemies in to avoid making it impossible to access the remaining runes without killing the ones that are already chained. The balancing is also very good, especially for your first jam, nearly every time I got cornered felt like a skill issue and not like I was getting screwed over by RNG.

Cons: the enemies spawning randomly will sometimes lead them to spawn right next to you which makes damage unavoidable due to RNG. There are two main methods to combat this, one is to have a reference to your player's transform either in a game manager or in the inspector if you find game managers too intimidating, and when you generate your random Vector2 (we'll call it spawnPos) just do:

if (Vector2.Distance(spawnPos, playerPos) < safeDistance)

{

       //run randomizer again

}

or my personal favorite method using Physics2D.Overlap:

//constant string for player tag so that you don't mistype it

private const string player = "Player";

foreach (Collider2D col in Physics2D.OverlapSphereAll(randomPos, safeRadius)

{

      if (col.tag == player)

       {

               //run randomizer again

       }

}

or if you want to avoid strings you can attempt GetComponent on a script that's only on your player:

foreach (Collider2D col in Physics.OverlapSphereAll(randomPos, safeRadius)

{

       PlayerMovement player = col.GetComponent<PlayerMovement>();

        if (player != null)

        {

               //run randomizer again

        }

}

these can be further optimized with things like while loops and separated methods to do a lot of the math more efficiently but I've already gone long-winded (if you're an experienced game programmer who already knows these techniques or even better ones and just didn't have time then I apologize for the tangent, however, if you're new to programming and ever want a hand optimizing scripts feel free to hit me up on Discord AudioDread#7997 for advice/review with future games. I'm always happy to help the community.

The only other con I have (and as a professional sound designer I may be a bit biased) I would have really loved to see some less stock sound effects. I HIGHLY recommend for your future games that you learn the basics of a DAW (digital audio workstation) such as Reaper, Nuendo, or even Audacity as a free option. Just learning the basics of layering and using really simple plugins is all you really need to at least put a unique spin on your games and just a few minutes of editing a stock sound effect to make it sound more distinct can make a world of difference.

THANK YOU THANK YOU THANK YOU

This is so much more productive than the feedback I get anywhere else.  I'm taking notes about DAW.  Of the three you recommended, which one is the best for beginners?

One other thing: enemy spawns

To avoid redoing the spawning math, what if I just moved the enemy away from the player if it spawns too close?  Like first check the position relative to the player, and if it's too close then move a random amount of steps away, pointed towards the origin so that it doesn't just move outside of the arena.

if(Vector2.Distance(spawnPos,playerPos) < safeDistance)
{
    transform.position = Vector2.MoveTowards(transform.position, new Vector2(0,0), Random.Range(5,10);
}

I know the processing power required to do a spawn is so incredibly small that the probability of it actually affecting performance by repeatedly failing checks is one in a million, but I'm just thinking generally for larger games and heavier processes here.

(+1)

So this heavily depends on which method you use and the engine you're using. Unity Engine is heavily optimized for physics calculations so even using the Physics2D.Overlap method takes an absurdly small amount of processing power. The resources taken to instantiate the object will be greater than any number of physics checks that could occur. As you expand the scope of the game to be larger and contain heavier processes, you're catering to heavier hardware. Essentially the scope will always balance itself out for this process.

This is due to Unity having a really efficient physics engine, from what I've heard (I don't use it so don't quote me on this) engines like Godot are less optimized out of the box for physics when compared to Unity so at that point doing a physics check might become unsustainable, but this would be quickly remedied by simply using a game manager to track the player position as in the other method. Then you're simply doing a very basic math calculation comparing two float values which is one of the least heavy processes you can possibly do.

I can see why you would propose your method as being more efficient, but it has a few distinct issues:

one is that it messes up your randomization. You're now statistically favoring the center of your screen because there are two instances where your enemies will spawn near the center point (1 if it randomizes towards the center and another where it randomizes on the player and gets moved towards the center) thus doubling the odds of a center spawn which will be noticeable. This could be mitigated by moving in a random direction and then clamping the value to not be allowed outside of the bounds, but because you're moving a physics object using its transform you would also have to calculate the new position BEFORE you move it and adjust on a single frame and you can see how that quickly becomes more complicated than simply recalculating the spawn.

Next is scalability. You mention this method to optimize for other larger projects but in reality, your method is only effective for a limited batch of use cases. Let's say you're designing a 3D game with an arena of multiple rooms where you're using a NavMesh to determine the spawn location in a reachable area and you're spawning randomly within a certain radius of the player. 0,0 may not be the exact center of the map and with multiple rooms, there may not be a clear path from the current point to your origin point. So you can see how the method I proposed would still work in this use case as it would simply re-run the original spawn method a second time which means it's automatically adapted to the specific game you're making. It's always best to develop a system that will work in EVERY game you make when possible, if it's scalable it's probably better.

Third, let's talk about utility. At first glance, your method seems fine but it actually has a very big and obvious (not a dig) flaw that you didn't notice. What if the player is already at the center of the screen? Where will the enemy go? Now you have to calculate the distance from the center as well as the distance from the player and do MORE math to determine where it should go INSTEAD of the center after you've done even more checks. Should you have noticed this? Of course not! You can't possibly notice everything. But then what happens? You commit to this system and you have to fix this issue when you notice them AFTER it's implemented. This perfectly illustrates a very important principle of programming; less is more when it comes to systems. If you have one system to spawn enemies and a completely independent system to move those enemies if they spawn in an undesirable location then you have twice the number of systems to debug and iron out, if you simply run the same system twice then you only have to focus on finding and fixing the issues of one system which makes your game much more manageable and greatly reduces the risk of having to completely rework your code because a system you implemented causes game breaking issues you couldn't have foreseen much later in development.

Lastly, your method is simply inconvenient. And it's 100% okay to sacrifice small optimizations for the sake of convenience, devs do it ALL THE TIME. You yourself even did it without realizing it. Moving a physics object using a transform instead of a Rigidbody actually has a much higher performance cost (it's been reduced by some changes in the Unity Engine but it's still significant) however using Vector2.MoveTowards is so much more convenient than calculating the direction yourself to plug it into the RigidBody, that for AI like what you have in your game, I and every other dev I know use Vector2.MoveTowards instead. The performance cost is not significant enough to justify the extra effort and you're much better off putting that energy into aspects of the game that are more consequential.

In summary, the fewer systems you use and the fewer lines of code in those systems is almost always going to be better even if it's a slightly more expensive method. I hope my long-winded explanation has been informative haha