jonascarpay / apecs

a fast, extensible, type driven Haskell ECS framework for games
391 stars 43 forks source link

Need help getting Enemy AI to follow player #98

Open ParetoOptimalDev opened 1 year ago

ParetoOptimalDev commented 1 year ago

I'm new to game development so the first thing I did was find a few tutorials, but most seemed to be unity and pygame focused. I found the basic idea was something like this per stackoverflow:

dir.x = player.x - enemy.x;
dir.y = player.y - enemy.y;
hyp = sqrt(dir.x*dir.x + dir.y*dir.y);
dir.x /= hyp;
dir.y /= hyp;
enemy.x += dir.x*speed;
enemy.y += dir.y*speed;

I tried to write the Haskell equivalent like:


moveTowardsPlayer :: Position -> Position -> Position
moveTowardsPlayer playerPos'@(Position pv@(V2 pX pY)) aiPos@(Position aiv@(V2 aiX aiY)) = do
  -- from https://stackoverflow.com/a/2625107
  -- float xDistance = playerX-enemyX;
  let xDistance = pX - aiX
      -- float yDistance = playerY-enemyY;
      yDistance = pY - aiY
      -- float hypotenuse = sqrt((xDistance * xDistance) + (yDistance * yDistance));
      hypotenuse = sqrt ((xDistance * xDistance) + (yDistance * yDistance))
      timeFactor = 0.1
   in if hypotenuse < 400
        then
          let yPos = timeFactor * 200 * (yDistance / hypotenuse)
              xPos = timeFactor * 200 * (xDistance / hypotenuse)
           in Position (V2 xPos yPos)
        else aiPos

My understanding is that written like this you wouldn't use triggerEvery like some apecs code uses. I also observed that most other gamedev tutorials seemed to not have something like triggerEvery and it seems like a big difference when trying to map gamedev tutorials to apecs. Any other concepts like this that are different in apecs than most gamedev tutorials?

But that doesn't work and the enemy moves away from the player sort of? I also started trying to debug it by writing out the position the enemy moves to but that hasn't been enough.

Finally I just thought I'd write out my problem in an issue and provide a reproducible repo that demonstrates my issue:

https://github.com/ParetoOptimalDev/apecs-tag-example

dpwiz commented 1 year ago

There's nothing apecs-specific. And it is much cleaner with vector types. Get a direction vector between your two points and scale it by desired velocity. Add to the original position:

move velocity current target = current + normalize (current - target) ^* velocity
jonascarpay commented 1 year ago

@dpwiz is right, you're doing a lot of vector math manually that a library like linear will allow you to express as vector math directly. That part isn't really apecs-specific.

Where apecs comes in is when you express that operation as a cmap over enemy positions. The easiest thing is just to have a global step function that's called once per frame, and part of that step function is the logic above. Some ECS libraries want you to get fancy with it and model each of these behaviors as if they ran on a separate dedicated thread, but for now that's completely unnecessary.