How to check for all possible collisions (architecture)?

Started by
7 comments, last by Norman Barrows 7 years, 4 months ago

Guys, I realize that I ask about too much stuff these days, but internship deadlines are coming and I need to send some games. It's a decent excuse.

My problem is that I can't come up with a good way to code my shoot() function in my Player class.( 3d fpshooter )

What I need is to somehow say: if the player has clicked on the left mouse button, check if the ray that goes out straight from his crosshair has intersected with an enemy object, and if it is, inform the monster who was hit that he needs to die in hell (or to decrease hp).

And the actual problem is that if I do it this way, I need access to all objects in every character that shoots, this means if I have 20 characters, every character needs to keep a pointer to all other characters so it can check if collision with any of the objects will return true... and it gets messed up because I have 20 pointers pointing to the same stuff.

What to do, bros? :huh:

( shoot() function can't take arguments, because it's called in a virtual void update() function )


#include "player.h"


Player::Player( glm::vec3 pos, Input *inputManager, std::vector<Model*> anims, std::vector<Shader*> shaders, std::vector<GameObject*> *gameObjects )
       :Character( anims, shaders )
{
    this->inputManager = inputManager;
    objects = gameObjects;
    mainCam.setCurrentPosition( pos );
}

void Player::update()
{
    rememberPreviousState();
    handleInput();
    lookAround();
    Move();
    shoot();
}

void Player::shoot()
{

}

void Player::handleInput()
{
    //Player movement
    abilities[ int( Ability::MOVE_LEFT      ) ] =  inputManager->getKeyState( SDLK_a );
    abilities[ int( Ability::MOVE_RIGHT     ) ] =  inputManager->getKeyState( SDLK_d );
    abilities[ int( Ability::MOVE_FORWARD   ) ] =  inputManager->getKeyState( SDLK_w );
    abilities[ int( Ability::MOVE_BACKWARDS ) ] =  inputManager->getKeyState( SDLK_s );

    if( inputManager->verticalAngle >  F_PI/2.0f ) { inputManager->verticalAngle =  F_PI/2.0f; }
    if( inputManager->verticalAngle < -F_PI/2.0f ) { inputManager->verticalAngle = -F_PI/2.0f; }

    mainCam.yawAngle   = inputManager->horizontalAngle;
    mainCam.pitchAngle = inputManager->verticalAngle;

}

void Player::lookAround()
{
    mainCam.setDirectionRight( glm::vec3( cos( mainCam.yawAngle ), 0.0f, sin( mainCam.yawAngle ) ) );
    mainCam.setDirection( glm::normalize( glm::vec3(
                                    cos( mainCam.yawAngle - F_PI/2.0f )*cos( mainCam.pitchAngle ),
                                    sin( -mainCam.pitchAngle ),
                                    sin( mainCam.yawAngle - F_PI/2.0f )*cos( mainCam.pitchAngle ) ) ) );

    moveHelperCam.setDirection( glm::vec3( cos( mainCam.yawAngle - F_PI/2.0f ), 0.0f, sin( mainCam.yawAngle - F_PI/2.0f ) ) );
    mainCam.setDirectionUp( glm::cross( mainCam.getDirectionRight(), mainCam.getDirection() ) );
    mainCam.setCurrentPitch( glm::angleAxis( mainCam.pitchAngle, glm::vec3( 1, 0, 0 ) ) );
    mainCam.setCurrentYaw( glm::angleAxis( mainCam.yawAngle, glm::vec3( 0, 1, 0 ) ) );

}

Advertisement

Have your World or Simulator do a line intersection test. It decides what, if anything, intersected that line. Then it returns a collection of zero or more objects that intersected it. It should also test for world obstructions like impenetrable walls and such. If anything intersected it, you find the first one.

If it intersects, you do whatever processing is appropriate for the event, like broadcasting an event that a player was hit.

Appropriate objects should be listening for that event. Destructable objects --- including characters --- should be listening for events that say they were hit and take appropriate action.

If the character dies as a result of that event, it should broadcast an event saying they died and pass along the appropriate information for the character's death, such as the source of damage, the amount of damage, the thing that was killed, the thing that did the killing, and whatever else you need.

Appropriate objects should be listening for that event. When they get the "player died" event they can look at the pointers in the event structure and take their own appropriate action.

These systems are known by many names, an event bus or message bus or broadcast system or event listeners, and are near-universal in games.

frob, thanks very much for answering , there is stuff that is quite complicated( like the store, proxy, cache, loader stuff ), but I think I understood what you suggested, basically you want me to implement some messaging system that just sends messages to other objects (for example, the observer pattern) and you don't like the idea of every object knowing about everybody else, because it's not effective.

But the thing is that you have programmed too much and you know what is good in which situations, for me it's not clear at all and I don't understand why to even use such patterns, seems like an overkill to me. That's why I want to start with something simple that sounds logical to me and then if a problem happens, I will rewrite it, it's not the end of the world.

But I like your idea of one class that checks for collisions and just notifies. I will create a Collisions class, because it just checks for collisions. One thing per class, right?

And then I will try to optimize it as much as I can, by that I mean that I don't want to check for all collisions every frame, I will check if there is a chance to collide, and only if there is, check for that collision.(will figure out how to do that later, for now I have enough cpu cycles. )

And when collision between 2 guys happens (normally a bullet and a guy), use their pointers and call their onCollision functions, right?

Looks like this:


class Collisions
{
  std::vector<GameObject*> gameObjects;//Holds pointers to all game objects.

  public void detectCollision()
  {
    for( auto& object : gameObjects )
    {
      //some optimizations so I don't check too much collisions, I will come up with it later.
      if( hasCollided(bullet, badguy ) == true )
      {
        badguy->OnCollision( bullet );
        //and maybe some other info later on.
      }
    }
  }
} 

Is it ok? ( you don't have to go into details, just as overall, I know that there are probably hundreds of better ways, but I need to try something in order to learn that it doesn't work, right? Does this look like a good/mediocre start? :wacko: )

You can break the problem down into steps if that helps.

Step one: cast a ray from the player to wherever he is aiming(assuming you're using hitscan-style weapons.)

-Some class needs to handle this, as frob pointed out it probably makes sense for the World class or maybe a Scene class or a GameLogic class or something to know how to exhibit that functionality(depends on your design.) If the player had to know how to cast a ray then you have the issue of the player having to know all about the scene and all the other characters in it.

Step two: resolve the collision.

-Collisions are all about spatial partitioning, if you can't guarantee an object isn't in the same space(trees or similar) you'll have to use a code check to compare them. That said the simplest answer to a problem is often the easiest one, for every raycast you'll be checking(n) characters for collisions(or n-1 if your function excludes the player.) If your game doesn't have that many characters it may be fine to just do all those checks. If you want to use something like octrees then worry about it when it becomes an issue.

Step three: do something with the results.

-When you fire the weapon certain systems will need to know about who was hit. An enemy will either have to be directly told it was hit, and then the enemy decides how much damage it takes based on information about the hit, or some third party class will do that decision making and affect the enemy class, either way you get the same result.

But other systems might be interested as well, you might want a hit marker to show up(cod style) that's a UI change. You might play a sound when the bullet impacts something(a wall or an enemy,) you might have blood or dust particles show. So who tells them? The simplest approach would probably be to have a mediator class(game logic) the logic knows when you fire it needs to cast a ray(it asks the world to cast a ray) it then receives results on who/what was hit and acts accordingly, informing them. It can also talk to the other major subsystems to cause the effects I mentioned prior to happen. That's one way to do it.

Another way is like how frob suggested, if we think of the gamelogic as a telling system then we can think of an event bus as a listening system. When you set up the game you hook up different systems to begin listening for messages they may be interested in(observer pattern or variants of it.) The nice part about this is everything is basically automatic once the game starts and new things can be added easily. Someone fired a gun? That could create a message saying someone fired a gun, that message could be picked up by an achievement system, the sound system to play the gunfire sound, an ai system to alert nearby creatures to the noise. The point is the system is flexible. That said it is also more complex in many ways and it is just a generic system, you still have to decide where you want the code to actually live. You could just as easily create an event system where the game logic still does all the processing, it just gets informed by event messages instead of direct function calls.

There are countless excellent messaging systems freely available online, so there isn't even a need to write one from scratch.
Just write something.


If it works, great!

If it doesn't, try to identify why. Then look for a better solution.

Rinse/repeat for N years and you'll have sufficient experience to just choose good solutions up front.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

frob, thanks very much for answering , there is stuff that is quite complicated( like the store, proxy, cache, loader stuff ), but I think I understood what you suggested, basically you want me to implement some messaging system that just sends messages to other objects (for example, the observer pattern) and you don't like the idea of every object knowing about everybody else, because it's not effective.

But the thing is that you have programmed too much and you know what is good in which situations, for me it's not clear at all and I don't understand why to even use such patterns, seems like an overkill to me. That's why I want to start with something simple that sounds logical to me and then if a problem happens, I will rewrite it, it's not the end of the world.


The most important thing to take away from Frob's post is simply this: the player doesn't need to know about all other characters in order to resolve the 'shoot' functionality. It just needs access to one object that knows about all the characters, and that object can resolve the shooting functionality.

That is one of the fundamental solutions in all of programming - whenever you find yourself thinking, "aahhh, all my X potentially need to know about all of Y", you need to introduce a new object B that is responsible for that process. (It doesn't necessarily have to be a completely new object, if an existing one can sensibly hold the functionality - but the key point is that you centralise the logic.)

The short answer: characters don't examine all the other characters and objects to see what they shot. The world (or some other central, shared class) does that, and tells the shooter what they need to know about the outcome. (It can also implement the outcome, too. No reason why the original shooter should have to work out how much damage was done or remove the victim from the world, etc.)

And when collision between 2 guys happens (normally a bullet and a guy), use their pointers and call their onCollision functions, right?


Normally there are 2 types of weapon projectile:

  • those that are resolved instantly, with a raycast - they don't need adding to any sort of list. Just check what they hit, and then they're done.
  • those that travel through the world, with a physical object - they can be added to some sort of collision list.

Make sure you know what type you're dealing with.

Also, if your collision resolving class iterates over all the game objects, it probably doesn't need its own copy of that list. It can probably access it from whatever the central source of game objects is. (The world? The map? The game? Usually there is a central object responsible for that.)

Guys, thanks again for the answers. I kind of fixed it, so I think the problem is solved. ^_^

That is one of the fundamental solutions in all of programming - whenever you find yourself thinking, "aahhh, all my X potentially need to know about all of Y", you need to introduce a new object B that is responsible for that process. (It doesn't necessarily have to be a completely new object, if an existing one can sensibly hold the functionality - but the key point is that you centralise the logic.)

Okay, I'll try to remember that for next time.

There are countless excellent messaging systems freely available online, so there isn't even a need to write one from scratch.

Where? I see only some JMS program that seems to establish communication between different applications, not objects/classes.

What I need is to somehow say: if the player has clicked on the left mouse button, check if the ray that goes out straight from his crosshair has intersected with an enemy object, and if it is, inform the monster who was hit that he needs to die in hell (or to decrease hp).

// get the mouse button

getmouse(&x,&y,&b)

// if left mouse button pressed

if b==1

{

// cast ray from camera, return ID of entity hit, or -1 for none.

tgt=raycast()

// return if they didn't hit anything

if tgt==-1 return

// apply damage

damage_tgt(tgt)

}

complications can arise depending on how the code is organized.

here, the cast uses the camera as the start location and direction, and the list of entities as the 'scene" into which the ray is cast. so the list of entities is the basic data structure,and raycast is type of search that can be performed on the entities list. damage_tgt is likewise a method that works on the entities list. if you use ECS, these methods would then call the location or damage component systems to do their jobs. damage_tgt would in turn call kill_tgt if the target took sufficient damage. note that this does not use any messaging, its all immediate processing.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

This topic is closed to new replies.

Advertisement