Page 1 of 1

shooting on terrain with collisions

Posted: Mon Apr 11, 2005 9:25 pm
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

Posted: Mon Apr 11, 2005 10:51 pm
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.

Posted: Mon Apr 11, 2005 11:26 pm
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!

Posted: Tue Apr 12, 2005 1:38 am
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

Posted: Tue Apr 12, 2005 1:39 pm
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?

Posted: Tue Apr 12, 2005 2:14 pm
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!

Posted: Tue Apr 12, 2005 2:29 pm
by dopyiii
That last post we me. :roll:

Posted: Tue Apr 12, 2005 3:32 pm
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...

Posted: Tue Apr 12, 2005 4:52 pm
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.

Posted: Tue Apr 12, 2005 5:02 pm
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++;
		}
	}
}

Posted: Tue Apr 12, 2005 6:08 pm
by dopyiii
Thanks for the help! I'll give it a try tonight. :)

Posted: Wed Apr 13, 2005 7:02 pm
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!

Posted: Thu Apr 14, 2005 12:01 am
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??? :?

Posted: Thu Apr 14, 2005 3:01 pm
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!