Terrain <-> player collision

I've a very difficult problem. The terrain I'm using for my game can't be compared with for example the terrain that's generated by the jME terrain maker. It has a lot of hills and mountains (I'll post a screenie later). Now I've several problems:


  1. I want that the player walks on the terrain (gravity). I've heard something about rays and intersection, could somebody clarify this for me?
  2. Sometimes, I want that a player cannot walk on a certain piece of terrain (hill to steep, may not walk into water, etc…). I've been thinking of collision with invisible meshes, but, does jME support that? And, what kind of algorithm do I need to implement to make sure the character cannot move trough these meshes?



    I do not wish to use physics to accomplish these goals. I've tried once to use physics, and the effects where horrible (very bouncy player, unable to rotate player, etc…).



    Can anyone help me?

Here’s the picture, notice the big mountain

For the first point, you can keep the player character (PC) atached to terrain obtaining terrain height (y) for the current (x, z) coordinates of the PC and setting (y) according to the value you got. There is already a method to get terrain height for a couple (x, z), browse the API documentation and you will find it.



For the second point, i am not expert. But yuo could use a Warcraft III like method. You can preset pathable and non pathable squares using a separate file to store this informations. Depending on the resolution of the squares grid you will get more or less accurate results.

I think that this method is used also in World of Warcraft, even if everything seems apparently smooth. I guess this becouse I noticed that there are really steep paths that are pathable, because they lead to a reachable area, while other escarpments with about the same inclination are not pathable.

2) Sometimes, I want that a player cannot walk on a certain piece of terrain (hill to steep, may not walk into water, etc...).


Or You could get the hight of the terrain one step ahead before moving the unit and if the difference to where it currently is is to big then don't move the unit.
The advantage is You can adjust the players ability to climb mountains with skill or use different modifiers for different units.

Endar, can you tell me where to find that method in the api? Note that my terrain is a preloaded model.



For the second question, I've thought about using this. In games with simple maps, such as RTS games, this is a good method. But i'm making a third person game, and I want to use caves, buildings, etc…



winkman, that's a good idea, if I find the method for getting the terrain height. I believe the best option would be a Ray, and checking where it intersects.



EDIT: i've seen somewhere on this forums an algorithm to move the player to the Y location where an ray intersected the terrain, but it seems I cannot find it anymore. Could anyone give me a link?

After some thinking, I came up with this



Surround all "forbidden" terrain (ie too steep hills, rocky terrain, water, etc) with invisible meshes. Next make an invisible box mesh that surrounds my player. When moving, move the invisible mesh first, check whether it collisions with "forbidden" terrain, and if it doesn't, move the player.



But, how do I implement this?

winkman said:

2) Sometimes, I want that a player cannot walk on a certain piece of terrain (hill to steep, may not walk into water, etc...).


Or You could get the hight of the terrain one step ahead before moving the unit and if the difference to where it currently is is to big then don't move the unit.
The advantage is You can adjust the players ability to climb mountains with skill or use different modifiers for different units.


This is a good idea... but does it consume a lot of resources or not?

And I have a personal question about it. Can be a good approach for a MMORPG?

SeySayux said:

Endar, can you tell me where to find that method in the api? Note that my terrain is a preloaded model.


So, it is not a TerrainBlock? You can try casting a ray then.
Anyway there are 4 methods in the TerrainBlock class: getHeight(float x, float z), getHeight(Vector2f position), getHeight(Vector3f position) and getHeightFromWorld(Vector3f position). I do not know if there are similar methods in Node or Geometry classes.

ok…ive replied to several posts about this issue. try search the forum and ull find my code for intersection detection.



i do have a updated version of that method which is slightly more efficient, and ill try to post it later

I've been thinking of the following method to move a player:

Move the invisible box;

Move it to the appropriate height with neakor's method

Check if the distance is higher than 0.5 m; if true, consider it too steep and deny the movement

Check if it's lower than -0.5 m; if true, consider it too low to walk, and jump instead (smooth aninamtion)

Check if it collisions with an forbidden terrain mesh; if true, deny the movement

If all tests pass; move the character.

sry i didnt reply to ur hill question.



if u dont want the player to walk into the water or the hills. simply cast a ray from where the player is to the destination the player is moving towards.



then define a final float as the minimum distance the player can get with the non-walk through models.



and check the distance if the player moves. if the distance is greater than the defined min, let the player walk, if the distance smaller, stop the movement.



if the hill is really steep, the distance will definately be less than the min. so u cant walk up or walk into it.



and for the water, just put a tall straight up model at the shore. and apply an alpha to it to make it invisible. for the same reason, the player cannot walk into the water anymore. well its actually coz the player cant walk through the invisible wall.

The distance is smaller when it gets steeper? I don't think so (see the graph hereunder)


      /
     /
    /
   /
  /        


steep      not steep

hmm… in pseudo code this would become:

public Vector3f goal;

public void move(float x, float z) {
//set the goal
goal.set(characterNode.getLocalTranslation().x + x, characterNode.getLocalTranslation().y + 2f, characterNode.getLocalTranslation().z + z);
goal.y = getCollisionPoint(goal);

//check whether to steep
if (hillTooSteep(new Vector3f(characterNode.getLocalTranslation(). x, characterNode.getLocalTranslation().y + 2f, characterNode.getLocalTranslation.z),new Vector3f(goal.x, characterNode.getLocalTranslation().y + 2f, goal.z))) return;

// check wheter we should jump
if (shoudJump(characterNode.getLocalTranslation(), goal)) {jumpGoal.set(goal); isJumping = true; return; }

// move the character
characterNode.setLocalTranslation(goal);
}

i think u didnt get what i was talking about.



u c, when u have no steep, the distance is infinity~



this way works fine in my game~ and in my game, everything is 3ds model… :stuck_out_tongue:

I believe I don't understand you  :?

SeySayux said:

The distance is smaller when it gets steeper? I don't think so (see the graph hereunder)


So… once the ray intersects, it stops, or what? I was thinking of measuring the distance between the player and the goal, and not the player and first collision.

SeySayux said:

So... once the ray intersects, it stops, or what? I was thinking of measuring the distance between the player and the goal, and not the player and first collision.


if the distance is less than a defined number, stop~

What would be a good distance? My player is 1,2,1.

hmm… i just stumbled across this topic http://www.jmonkeyengine.com/jmeforum/index.php?topic=5363.0 and i've found TestObjectWalking… so I'm going to see if I can write some collision detection handler.

Would this be a good collision detection method?


 public float getCollisionPoint(Vector3f goal) {
Vector3f intersection = new Vector3f();
Ray ray = new Ray(goal , new Vector3f(goal.x, goal. y -1f, goal.z)
PickResults results = new TrianglePickResults();
// Use the ray to find all the models on the player's island which are hit by the ray.
results.clear();
terrainNode.findPick(ray, results);

// Find the target model in the results.
boolean found = false;
boolean hit = false;
for(int i = 0; i < results.getNumber() && found == false; i++)
{
TriMesh model = (TriMesh)results.getPickData(i).getTargetMesh().getParentGeom();
if(model.getName().equalsIgnoreCase(terrainNode.getName())
{
found = true;
// Find the intersection where the ray hits the target's boundingBox in world
// coordinate system.
Vector3f[] vertices = new Vector3f[3];
for(int j = 0; j < model.getTriangleCount() && hit == false; j++)
{
model.getTriangle(j, vertices);
hit = ray.intersectWhere(
vertices[0].addLocal(model.getWorldTranslation()),
vertices[1].addLocal(model.getWorldTranslation()),
vertices[2].addLocal(model.getWorldTranslation()), intersection);
}
}
}
// If the target cannot be found or the ray does not hit the target, return null.
if(found == false || hit == false)
return null;
// Return the intersection.
return intersection.y;
}