shooting on terrain with collisions

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
dopyiii
Posts: 26
Joined: Wed Mar 23, 2005 7:48 pm

shooting on terrain with collisions

Post by dopyiii »

Hey all, I could use a little help figuring out how to shoot on terrain. I've got the code form the techdemo working (sort of).

I have three problems:

First, when I shoot into the air (or not at the terrain), I can't see my shot. Well, I can initially because it starts in front of my nose, but I don't see it fly away like it should. I noticed that it does this on the techdemo as well.

Second, when shooting at the terrain, it's like the shot never collides with the terrain, but just keeps flying through.

Third, when I shoot multiple times, my audiere stream for the shooting sound won't play until the current sound is done playing.

Can anyone give me a little help? Feel free to point out the obvious as I'm sure it's something easy that I overlooked...

Thanks!

Dopyiii
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

For help with 2, we'd need to see some code. For 1, I'll look into that, but that should have nothing to do with the selector, or terrain. And sorry, I can't help you with #3.
Image
dopyiii
Posts: 26
Joined: Wed Mar 23, 2005 7:48 pm

Post by dopyiii »

I thought you might want some code instead of reading my mind :wink: Here's the (probably very familiar) code that I'm using (if you need something else let me know):

Code: Select all

void View::shoot()
{
	scene::ISceneManager* sm = device->getSceneManager();
	scene::ICameraSceneNode* camera = sm->getActiveCamera();
	core::array<SParticleImpact> Impacts;

	if (!camera || !selector)
		return;

	SParticleImpact imp; 
	imp.when = 0;

	// get line of camera
	core::vector3df start = camera->getPosition();
	core::vector3df end = (camera->getTarget() - start);
	end.normalize();
	start += end*5.0f;
	end = start + (end * camera->getFarValue());
	
	core::triangle3df triangle;
	core::line3d<f32> line(start, end);

	// get intersection point with map
	if (sm->getSceneCollisionManager()->getCollisionPoint(line, selector, end, triangle))
	{
		// collides with wall
		core::vector3df out = triangle.getNormal();
		out.setLength(0.03f);

		imp.when = 1;
		imp.outVector = out;
		imp.pos = end;
	}
	else
	{
		// doesnt collide with wall
		end = (camera->getTarget() - start);
		end.normalize();
		end = start + (end * 1000);
	}

	// create fire ball
	scene::ISceneNode* node = 0;
	node = sm->addBillboardSceneNode(0,
		core::dimension2d<f32>(25,25), start);

	node->setMaterialFlag(video::EMF_LIGHTING, false);
	node->setMaterialTexture(0, device->getVideoDriver()->getTexture("../media/fireball.bmp"));
	node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
		
	f32 length = (f32)(end - start).getLength();
	const f32 speed = 0.9f;
	u32 time = (u32)(length / speed);

	scene::ISceneNodeAnimator* anim = 0;

	// set flight line

	anim = sm->createFlyStraightAnimator(start, end, time);
	node->addAnimator(anim);	
	anim->drop();

	anim = sm->createDeleteAnimator(time);
	node->addAnimator(anim);
	anim->drop();

	if (imp.when)
	{
		// create impact note
		imp.when = device->getTimer()->getTime() + (time - 100);
		Impacts.push_back(imp);
	}

	if(sfx)
	{
		attack->play();
	}
}
I don't remember making any changes to this code. I think that the only real difference is that I'm using an ITerrainSceneNode (here it's called "selector") instead of the Quake level node stuff that's used in the demo.

Right off, I see that I should probably put Impacts in my class, not just in this function...

I appreciate the help!
SARIN
Posts: 139
Joined: Fri Oct 29, 2004 3:53 am

Post by SARIN »

yeh, ive noticed the collision too. i think it has something to do with the terrain and getfarpoint
reason: it sends a line through the terrain at the far point. however, there is no far point if the camera is dealing with with the skybox, so the far point is set to the cam position, and that will be right at the veiwpoint.
the way to solve this is to not use the skybox scene node, or use some other way of getting the far point
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

I'll have to test this code out, I've never done collisions with projectiles like this, I've always checked possibles collision each render, to handle moving objects. What you're doing will only work for colliding with whats in the path of the projectile at the time it's fired.

From what I can see, with a quick glance over the code, it should work. You're selector is being properly initialized, right?
Image
Guest

Post by Guest »

Okay, I knew it had to be something dumb... :roll: I just had to make my Impacts array a class member instead of a local variable.

I still haven't figured out the whole shooting in the air problem... I didn't think that the skybox would interfere with getting a far point. How else could I do it?

And I think I can fix the whole sound problem assuming that Audiere is thread safe - I can just put the play function in a new thread each time I shoot. That should work, shouldn't it? Does anyone do it differently?

Spintz, my selector's working fine. I have it doing my gravity for me. Also, I'm curious how you do your projectile collision. I think that I'd be better off using something a little more robust than this (since I will have dynamic objects crusing around in my game). Could you show me how you do it?

Thanks!
dopyiii
Posts: 26
Joined: Wed Mar 23, 2005 7:48 pm

Post by dopyiii »

That last post we me. :roll:
cartoonit
Posts: 286
Joined: Mon Nov 15, 2004 6:36 pm

Post by cartoonit »

For the Audiere sound, you could reset it before the next time you play so something like:

Code: Select all

mEffect->reset(); 
mEffect->setRepeat(Repeat);
mEffect->setVolume(Volume);
mEffect->play();
This works for me...
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

dopyiii wrote:Spintz, my selector's working fine. I have it doing my gravity for me. Also, I'm curious how you do your projectile collision. I think that I'd be better off using something a little more robust than this (since I will have dynamic objects crusing around in my game). Could you show me how you do it?
Well, what I did, was created a class for my projectiles ( bullets, rockets, etc. ) and they have an update function, which gets called every render with the time difference since the last render. I move the projectile based on it's speed and the time that has elapsed. First though, I calculate what it's new position would be, based on the time delta and it's speed and then check collision from it's current position to the new estimated position. If there's no collison, I update the position and move on to the next projectile, if there is a collision, you handle it appropriately.
Image
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

The sample code I have for this is from NX++, but you should get the concept from it and be able to port it to Irrlicht very easily.....

CRockets.h -

Code: Select all

#ifndef __CROCKETS_H__
#define __CROCKETS_H__

#include <nx++/irrlicht.h>
#include <nx++/NXRoot.h>

#include "CGameCore.h"

using namespace irr;

class CRockets
{
public:
	CRockets ( );
	~CRockets ( );

	void addRocket ( f32 size, f32 speed, f32 ttl );
	void update ( f32 timeDelta );

private:
	struct CRocket
	{
		~CRocket ( )
		{
			if ( ParticleNode )
				ParticleNode->remove ( );

			if ( SceneNode )
				SceneNode->remove ( );
		}

		scene::IAnimatedMeshSceneNode*		SceneNode;
		scene::IParticleSystemSceneNode*	ParticleNode;
		core::vector3df						Direction;
		float								Speed;
		f32									TTL;
	};

	core::list<CRocket*>	Rockets;
	scene::IAnimatedMesh*	RocketMesh;
	video::ITexture*		TrailTexture;

	gamecore::CGameCore*	GameCore;
};

#endif // __CROCKETS_H__
CRockets.cpp -

Code: Select all

#include "CRockets.h"
#include "CHUD.h"
#include "CTargets.h"
#include "CExplosions.h"
#include "CNinja.h"

CRockets::CRockets ( ) : GameCore(gamecore::CGameCore::GetInstance()), RocketMesh(0), TrailTexture(0)
{
	RocketMesh = GameCore->GetResourceManager()->getMesh ( "media/missile.x" );
	RocketMesh->grab ( );

	TrailTexture =  GameCore->GetResourceManager()->getTexture ( "media/Particle.bmp" );
	GameCore->GetVideoDriver ( )->makeColorKeyTexture(TrailTexture,core::position2d<s32>(0,0));
}

CRockets::~CRockets ( )
{
	Rockets.clear ( );
	RocketMesh->drop ( );
}

void CRockets::addRocket ( f32 size, f32 speed, f32 ttl )
{
	core::line3d<f32> line = 
		GameCore->GetCollisionManager()->getRayFromScreenCoordinates(GameCore->GetCursorControl()->getPosition());
	core::vector3df target;
	core::triangle3df triangle;
	if ( !GameCore->GetCollisionManager()->getCollisionPoint(line, GameCore->GetMetaSelector(), target, triangle ) )
	{
		target = line.end;
	}

	core::vector3df position;
	core::vector3df angle = line.getYXangle ( );
	position = GameCore->GetNinja()->getSceneNode()->getPosition();
	position.Y += GameCore->GetNinja()->getSceneNode()->getBoundingBox().MaxEdge.Y - GameCore->GetNinja()->getSceneNode()->getBoundingBox().MinEdge.Y;
	CRocket* rocket = new CRocket ( );
	rocket->Direction = target - position;
	rocket->Direction.normalize ( );
	rocket->Speed = speed;
	rocket->TTL = ttl;
	rocket->SceneNode = GameCore->GetSceneManager ( )->addAnimatedMeshSceneNode ( RocketMesh );
	rocket->SceneNode->setScale ( core::vector3df ( size, size, size ) );
	rocket->SceneNode->setPosition ( position );
	rocket->SceneNode->setRotation ( angle );
	rocket->ParticleNode = GameCore->GetSceneManager ( )->addParticleSystemSceneNode ( false, 0 );
	rocket->ParticleNode->setMode ( scene::EPSM_GLOBAL_EMITTER_LOCAL );
	rocket->ParticleNode->setMaterialTexture ( 0, TrailTexture );
	rocket->ParticleNode->setMaterialType ( video::EMT_TRANSPARENT_ADD_COLOR );
	rocket->ParticleNode->setParticleSize ( core::dimension2d<f32> ( 1.0f, 1.0f ) );
	core::vector3df direction = rocket->Direction;
	direction /= 3.0f;
	scene::IParticleEmitter* pe = rocket->ParticleNode->createPointEmitter ( direction, 2500, 
		2500, video::SColor ( 201, 255, 255, 255 ), video::SColor ( 201, 255, 255, 255),
		800, 1000, 2 );
	rocket->ParticleNode->setEmitter ( pe );
	pe->drop ( );
	Rockets.push_back ( rocket );
}

void CRockets::update ( f32 timeDelta )
{
	core::line3d<f32> line;
	core::triangle3df triangle;
	core::vector3df intersection;
	core::vector3df position;

	core::list<CRocket*>::Iterator rocketsItTemp;
	core::list<CRocket*>::Iterator rocketsIt = Rockets.begin ( );
	while ( rocketsIt != Rockets.end ( ) )
	{
		position = (*rocketsIt)->SceneNode->getPosition ( );
		position += (*rocketsIt)->Direction * ( timeDelta * (*rocketsIt)->Speed );

		if ( GameCore->GetMetaSelector ( ) )
		{
			line.start = (*rocketsIt)->SceneNode->getPosition ( );
			line.end = position;

			if ( GameCore->GetCollisionManager ( )->getCollisionPoint (line, 
				GameCore->GetMetaSelector ( ), intersection, triangle ) )
			{
				rocketsItTemp = rocketsIt + 1;
				delete (*rocketsIt);
				Rockets.erase ( rocketsIt );
				rocketsIt = rocketsItTemp;

				// check to see if it colliding with a box
				scene::ISceneNode* nodeHit = GameCore->GetCollisionManager ( )->getSceneNodeFromRayBB ( line );

				if ( nodeHit )
					if ( nodeHit->getID ( ) > 999 )
						GameCore->GetTargets ( )->RemoveTargetById ( nodeHit->getID ( ) );

				GameCore->GetExplosions ( )->addStandardExplosion ( intersection, core::dimension2d<f32>(7,6), 
					core::vector3df ( 0.0f, 0.0001f, 0.0f ), 50, 75, 
					video::SColor ( 225, 255, 0, 0 ), video::SColor ( 225, 255, 128, 0 ), 
					video::SColor ( 225, 0, 0, 0 ), 4000, 1000, 2500, 180, 3 );
				//GameCore->GetExplosions ( )->addLargeExplosion ( 6, intersection, core::dimension2d<f32>(7,6), 
				//	core::vector3df ( 0.0f, -0.003f, 0.0f ), 50, 75, 
				//	video::SColor ( 225, 255, 0, 0 ), video::SColor ( 225, 255, 128, 0 ), 
				//	video::SColor ( 225, 0, 0, 0 ), 4000, 1000, 1500, 45, 3 );
				continue;
			}
		}

		(*rocketsIt)->SceneNode->setPosition ( position );
		(*rocketsIt)->ParticleNode->setPosition ( position );
		(*rocketsIt)->TTL -= timeDelta;

		if ( (*rocketsIt)->TTL <= 0 )
		{			
			rocketsItTemp = rocketsIt + 1;
			delete (*rocketsIt);
			Rockets.erase ( rocketsIt );
			rocketsIt = rocketsItTemp;
		}
		else
		{
			rocketsIt++;
		}
	}
}
Image
dopyiii
Posts: 26
Joined: Wed Mar 23, 2005 7:48 pm

Post by dopyiii »

Thanks for the help! I'll give it a try tonight. :)
dopyiii
Posts: 26
Joined: Wed Mar 23, 2005 7:48 pm

Post by dopyiii »

Thanks for the code Spintz, but I'm too dumb to figure it out. Well, too dumb or too burned out by this project. Probably a little of both :wink:

One interesting thing that I found was that the techdemo way of shooting works fine if you have a collision with the map/terrain, whatever. But, if you don't (like shooting at the skybox, etc), then the animator actually moves backwards. I set the far value to be really big and when I shot at the skybox, sure enough it started at the far value and came back at me.

So, here's how I fixed it. In the if/else that gets the collisionPoint to test if it hits the map/terrain, in the else code I changed

Code: Select all

end = (start - camera->getTarget() );
to

Code: Select all

end = (camera->getTarget() - start);
Now, I can shoot blissfully off into space... :)

Of course, Spintz' way of doing stuff is probably better (okay, yeah, it IS better). If you get bored and want to translate Spintz' code into a generic "irrlicht only" code, that'd be super. But this quick and dirty method will work just fine for what I'm doing right now.

Thanks again for all of your help!
Spintz
Posts: 1688
Joined: Thu Nov 04, 2004 3:25 pm

Post by Spintz »

Give me some time, and I'll rewrite the code for both FPS and for 3rd person camera support.

What I find strange, is that in your original post of code, you were doing camera->target - start??? :?
Image
dopyiii
Posts: 26
Joined: Wed Mar 23, 2005 7:48 pm

Post by dopyiii »

Ooops. Reverse - switch that. It was end = (camera->getTarget() - start); and is now end = (start - camera->getTarget() );:oops:

I know that many people have asked how to shoot (despite this code in the techdemo). I'm sure that all noobs (especially me) would appreciate your rewritten code :D

I'm not in any real rush, but I'll look forward to seeing it.

Thanks Spintz!
Post Reply