Game Development Community

Continuous Laser

by Derk Adams · 06/15/2005 (12:23 am) · 132 comments

Download Code File

Discussion:
This resource uses the Torque Laser Beams resource as a starting point. So much appreciation is given to Robert Brower. The issues that I have with the previous resource are: no control of beam duration, long lived beams stop being attached to the weapon, firing at close range doesn't even display a beam.

To fix theses issues, I recreated the resource, removed a lot of unnecessary code, and made a small change to the projectile code. All issues with the previous resource are all connected to the fact that the laser projectile was "fired" from the weapon and "destroyed" upon collision. This resource allows the user to set the maximum length and duration of the laser beam. Also, the default projectile "forgets" its source object (to enable damaging the firer) after 7 ticks. To allow the laser to "remember" the originating object, I had to remove the constant and replace it with a variable.

To save network traffic, the laser is a single object that is moved each tick to align it with the targeting point. As such, it does not "explode" as the explosion method destroys the object. Instead the "particleEmitter" is used at the end of the beam. "Oncollision" is still called every tick to allow damage based on collision duration.

Comments:
I do not use "onCollision" to deal damage for this weapon because the points add up really fast. Instead, I check the firepoint "onFire" and deal damage just before creating the "projectile".
if (%scanTarg) {
    %targetId = getWord(%scanTarg,0);
    %targetPoint = getWords(%scanTarg,1,3);
    if (%targetId.getType() & $TypeMasks::ShapeBaseObjectType) {
      %targetId.damage(%obj,%targetPoint,%this.damage,"Laser");
    }
  }
There was an issue of object sorting when rendering since sometimes the beams are rendered behind the current scene sometimes (i.e. not visibly rendered), I have solved it by forcing the rendering even if not visible.
There was also an alpha problem carried over from the original resource when the beam overlaps a water block, I have fixed it in the prepRender function.

Key:
I use the same indication of modifications as a patch file. A line beginning with a "-" is to be removed and a line beginning with a "+" is to be added. Be sure to remove the "+" when you actually put the line into your code. A few lines around the changes are shown to give context to the changes. The triple dot "..." is showing that information has been removed for brevity purposes.

Development Environment:
June 2005
Head 1.2.2
Win32
Single Player

Implementation:
EngineFile: /console/consoleTypes.h
enum ConsoleDynamicTypes {

...
   TypeTriggerPolyhedron,

   TypeProjectileDataPtr,

+   TypeLaserDataPtr,

   TypeCannedChatItemPtr,

...
EngineFile: /game/projectile.h
class ProjectileData : public GameBaseData
{
...
public:
   // variables set in datablock definition:
   // Shape related
   const char* projectileShapeName;

+   U32 SourceIdTimeoutTicks;              ///< Time until Projectile can damage creator
...

class Projectile : public GameBase
{
   typedef GameBase Parent;
   ProjectileData* mDataBlock;

   SimObjectPtr<ShapeBase> mTarget; 
   S32 mTargetId; 

public:
   // Initial conditions
   enum ProjectileConstants {
-      SourceIdTimeoutTicks = 7,   // = 231 ms
      DeleteWaitTime       = 500, ///< 500 ms delete timeout (for network transmission delays)
      ExcessVelDirBits     = 7,
      MaxLivingTicks       = 4095,
   };
...
EngineFile: /game/projectile.cc
ProjectileData::ProjectileData()
{
   projectileShapeName = NULL;
+   SourceIdTimeoutTicks = 7;
...

void ProjectileData::initPersistFields()
{
   Parent::initPersistFields();

   Con::registerType("ProjectileDataPtr", TypeProjectileDataPtr, sizeof(ProjectileData*),
                     REF_GETDATATYPE(ProjectileData),
                     REF_SETDATATYPE(ProjectileData));

+   addNamedFieldV(SourceIdTimeoutTicks, TypeS32, ProjectileData, new IRangeValidatorScaled(TickMs, 0, Projectile::MaxLivingTicks));

   addNamedField(particleEmitter,  TypeParticleEmitterDataPtr, ProjectileData);
...

void ProjectileData::packData(BitStream* stream)
{
...
+   stream->writeRangedU32(SourceIdTimeoutTicks, 0, Projectile::MaxLivingTicks);
   stream->writeRangedU32(lifetime, 0, Projectile::MaxLivingTicks);
   stream->writeRangedU32(armingDelay, 0, Projectile::MaxLivingTicks);
   stream->writeRangedU32(fadeDelay, 0, Projectile::MaxLivingTicks);
...

void ProjectileData::unpackData(BitStream* stream)
{
...
+   SourceIdTimeoutTicks = stream->readRangedU32(0, Projectile::MaxLivingTicks);
   lifetime = stream->readRangedU32(0, Projectile::MaxLivingTicks);
   armingDelay = stream->readRangedU32(0, Projectile::MaxLivingTicks);
   fadeDelay = stream->readRangedU32(0, Projectile::MaxLivingTicks);
...

void Projectile::processTick(const Move* move)
{
   Parent::processTick(move);

   mCurrTick++;
-   if(mSourceObject && mCurrTick > SourceIdTimeoutTicks)
+   if(mSourceObject && mCurrTick > mDataBlock->SourceIdTimeoutTicks)
   {
      mSourceObject = 0;
      mSourceObjectId = 0;
   }
...
ScriptFile: /server/scripts/crossbow.cs
-datablock ProjectileData(CrossbowProjectile)
+datablock LaserData(CrossbowProjectile)
{
...
-   lifetime            = 5000;
-   fadeDelay           = 5000;
+   lifetime = 100;
+   fadeDelay = 100;
...
+   beamStartRadius = 0.1;
+   beamEndRadius = 0.1;
+   materialList = "~/data/shapes/laserbeam/red.dml";
+   interval = 0;
}
...

datablock ShapeBaseImageData(CrossbowImage)
{
...
+  maxRange = 500; // maximum range in meters
...

function CrossbowImage::onFire(%this, %obj, %slot)
{
   %projectile = %this.projectile;

   // Decrement inventory ammo. The image's ammo state is update
   // automatically by the ammo inventory hooks.
   %obj.decInventory(%this.ammo,1);

-   // Determin initial projectile velocity based on the 
-   // gun's muzzle point and the object's current velocity
-   %muzzleVector = %obj.getMuzzleVector(%slot);
-   %objectVelocity = %obj.getVelocity();
-   %muzzleVelocity = VectorAdd(
-      VectorScale(%muzzleVector, %projectile.muzzleVelocity),
-      VectorScale(%objectVelocity, %projectile.velInheritFactor));

+  //Determine how far should the picking ray extend into the world?
+  %selectRange = %this.maxRange;
+  %eyeVector = %obj.getEyeVector();
+  %eyePoint = %obj.getEyePoint();
+  // scale eye vector to the range the player is able to fire
+  %eyeScaled = VectorScale(%eyeVector, %selectRange);
+  // targetpoint = eye point + length of selectable range
+  %targetPoint = VectorAdd(%eyePoint, %eyeScaled);
+  // Everything mask
+  %mask = $TypeMasks::StaticObjectType|$TypeMasks::EnvironmentObjectType|$TypeMasks::TerrainObjectType|$TypeMasks::InteriorObjectType|$TypeMasks::WaterObjectType|$TypeMasks::TriggerObjectType|$TypeMasks::MarkerObjectType|$TypeMasks::GameBaseObjectType|$TypeMasks::ShapeBaseObjectType|$TypeMasks::CameraObjectType|$TypeMasks::StaticShapeObjectType|$TypeMasks::PlayerObjectType|$TypeMasks::ItemObjectType|$TypeMasks::VehicleObjectType|$TypeMasks::VehicleBlockerObjectType|$TypeMasks::ProjectileObjectType|$TypeMasks::ExplosionObjectType|$TypeMasks::CorpseObjectType|$TypeMasks::DebrisObjectType|$TypeMasks::PhysicalZoneObjectType|$TypeMasks::StaticTSObjectType|$TypeMasks::StaticRenderedObjectType|$TypeMasks::DamagableItemObjectType;

+  %scanTarg = ContainerRayCast (%eyePoint, %targetPoint, %mask, %obj);
   
+  if (%scanTarg) {
+    %targetPoint = getWords(%scanTarg,1,3);
+  }

   // Create the projectile object
   %p = new (%this.projectileType)() {
      dataBlock        = %projectile;
-      initialVelocity  = %muzzleVelocity;
+      initialVelocity  = "0 0 0";
-      initialPosition  = %obj.getMuzzlePoint(%slot);
+      initialPosition  = %targetPoint;
      sourceObject     = %obj;
      sourceSlot       = %slot;
      client           = %obj.client;
+      range = %this.maxRange;
   };
   MissionCleanup.add(%p);
   return %p;
}
Included files:
Create directory /data/shapes/laserbeam.
Copy red.dml and red_beam.png to that directory.
Copy laser.cc to /engine/game directory
Recompile engine

Credits:
Derk Adams - adamsderk@hotmail.com
Page «Previous 1 2 3 4 5 6 7 Last »
#1
06/15/2005 (3:54 am)
Wow! I was just going to impliment something similair tonight, though not nearly as complex... This saves me the effort. Thank you.
#2
06/15/2005 (6:19 am)
Fantastic. Will try it out. 5* resource.
#3
06/15/2005 (6:28 am)
Wow! I was just about to ask for a better laser resource (Eric Risser's model) but this came up just in time! I'll try it out later. :)
#4
06/15/2005 (11:20 am)
Cool cool cool, I'm impatient to try this resource, any screenshot available ?
#5
06/15/2005 (6:08 pm)
I too was going to work on a better laser resource thanks for saving me the time.
#6
06/15/2005 (6:46 pm)
Glad it is useful to so many people.

@ Pat: Check the original resource (link at top) for a screenshot. Visually, it is identical, I just changed the behavior.

Thanks.
#7
06/16/2005 (1:21 pm)
Cool... i so want this to work.

But after following the instructions and running,

I get trouble with this line:
%eyeVector = %obj.correctWeaponVector(%this, -1);

GUNS/server/scripts/laser.cs (940): Unknown command correctWeaponVector.
Object (1522) HoverVehicle -> Vehicle -> ShapeBase -> GameBase -> SceneObject -> NetObject -> SimObject

Can't find that function anywhere... do you have it?
#8
06/16/2005 (3:16 pm)
Jason,

Sorry, I tried to pull all of my other code out, but missed that.

Replace:
%obj.correctWeaponVector(%this, -1);
With:
%obj.getEyeVector();

I have updated the resource.

Thanks.
#9
06/16/2005 (3:41 pm)
Cheers...

I put that in, no complaints now..
Just not seeing a laser beam.

Any common gotchas you know of that would make it so I can't see the beam?
#10
06/16/2005 (4:42 pm)
Jason,

Ok, the big factors are in the projectile datablock:

lifetime = 100; // how long it stays on the screen
beamStartRadius = 0.1; // size of beam at the muzzle, try larger values
beamEndRadius = 0.1; // size of beam at the endpoint
materialList = "~/data/shapes/laserbeam/red.dml"; // make sure that this file is in the right place

Oh, and as gotchas...
Check in laser.cc to be sure the following line (and it's accompanying bracket) is commented out:
// if (state->isObjectRendered(this)) {
My initial version didn't have it removed.

Also, make sure that you have "maxRange = 500;" set in the image datablock as a value of 0 (or no value at all) will make a laser of 0 length.

HIH.
#11
06/16/2005 (8:23 pm)
Glad to see people are still getting something out of the work that Robert and I did, I wasn't sure if the rendering code was still working properly.

Thanks for updating it Derk, what you've implemented are things I always intended to do but I just never got around to it.
#12
06/16/2005 (10:13 pm)
Jeff,

That is what is great about this community, we get to stand on the shoulders of those who came before us. I could never have done it without your help in the first place.

Thanks for contributing.
#13
06/17/2005 (6:16 am)
Everyone,

I have solved the alpha issue when the beam overlaps the waterblock. In the prepRenderImage function in laser.cc, remove:
// Populate it.
      image->obj = this;
      image->isTranslucent = true;
      image->sortType = SceneRenderImage::BeginSort;
      // Insert it into the scene images.
      state->insertRenderImage(image);

      // Yes, so get a SceneRenderImage.
      image = new SceneRenderImage;
Note that this is the "BeginSort" part.

I have updated the resource so you can just redownload it for the correction.

Thanks.
#14
06/17/2005 (7:22 pm)
Would this resource work with TSE ?
#15
06/17/2005 (7:41 pm)
Tek0,

I have no idea. I don't have it to test on.

Thanks.
#16
06/18/2005 (3:25 am)
I'm still getting problems getting the beam to show. I'm using 1.4 head, I don't know if this is an issue... pretty standard, not modified much apart from getting particles to work.

I'd be interested to know if anyone has this working in the latest CVS Torque.

Or it could be that the beam isn't displayed for long enough... my laser weapon, based on the crossbow, is fired from a vehicle.. the blast is very short no matter how long I hold the fire button down for.. but if the beam duration is 100, I don't see how this can be a problem..
#17
06/18/2005 (6:48 am)
Ah Jason,

1.4 could be the issue, I'm on 1.2. My suggestion (although extra work) is to strip this out and try the original beam resource. If that works, then it is something between this and 1.4, if not then it is just a 1.4 issue.

Oh, and the duration is not determined by the length of key press, but the value in the datablock. If you need a variable duration, you willl need to use the beam resource.

You might also want to check Eric Risser's beam which is just a working example of the Beam resource.

Thanks.
#18
06/18/2005 (11:11 am)
Hmmm.. now you're just teasing :-( The beam looks really nice in the working example.

If anyone can get this working in 1.4, I'd be very happy (especially if they told me how to get it working)

I don't have a 1.2 codebase, but projectile.cc has changed a lot from 1.1 (the only other code base I have for comparison)
#19
06/19/2005 (11:05 am)
I just tried this resource on TSE and it doesn't work because this resource uses OpenGL and TSE uses DirectX. Could someone update the Laser.cc for TSE?
#20
06/20/2005 (11:10 am)
OK, I got it working :-D

You have to set the Projectile type of the weapon to Laser instead of Projectile.
No wonder I couldn't see a laser, the script wasn't creating one. it was creating a normal projectile...

Now I just need to find a way to keep it switched on, but that's probably more to do with my firing mechanism and state system than anything else.

Cheers for this, top resource.
Page «Previous 1 2 3 4 5 6 7 Last »