Idea: Caio Engine, UScript physics in UE1

Discussions about Coding and Scripting
Post Reply
User avatar
Gustavo6046
Godlike
Posts: 1462
Joined: Mon Jun 01, 2015 7:08 pm
Personal rank: Resident Wallaby
Location: Porto Alegre, Brazil
Contact:

Idea: Caio Engine, UScript physics in UE1

Post by Gustavo6046 »

Caio Engine, the 1999 Karma! This is just a idea that would probably make a nice revolution in UT. :P

We would have the following classes:

Code: Select all

Object -> Actor -> Info -> CaioOrganism
Object -> Actor -> CaioBone
CaioOrganism would store and process a entire Caio-object related data.
CaioBone's are linked lists. Each CaioBone store end and start vectors, a previous Caio Bone, a next Caio Bone and a float that determines how much can it turn around the start vector.

Preparing:
  • The actor we want to 'Caio' must NOT show up the parts we want to add physics to. These are shown by CaioBones.
  • The actor we want to 'Caio' must NOT emit sound each bone is meant to emit, they are as well emit by CaioBones.
Basically, this would be the workflow:
  1. The CaioOrganism actor is spawned by the actor we want to simulate Physics with.
  2. We call SetupCaio(self) in the spawned CaioOrganism object.
  3. Spawn a CaioBone for each piece you want that rotates independently.
  4. For each CaioBone set up the following properties:

    Code: Select all

    CaioBone PreviousBone* -> The bone this one is attached to.
    
    CaioBone NextBones[16]* -> A list of bone actors attached to this one.
    
    Class BoneActorSoundClasses[64]* -> The corresponding classes for each sound that should be played in BoneActorSounds. (sorry, can't formulate a better phrase)
    
    Sound BoneBSPSound* -> The sound you want the bone to emit when colliding with world geometry.
    
    Sound BoneActorSounds[64]* -> A array of Sounds. For each sound, it will be emitted if the bone collide with the actor of matching class in BoneActorSoundClasses[sound index]. If the class does not match any, will play BoneActorSoundGeneric.
    
    Sound BoneActorSoundGeneric* -> A Sound played if it collides with a actor which class does not match any in BoneActorSoundClasses.
    
    float MaxFlexibility -> Determines how much can this bone rotate around the Start vector. More than this and it'll stop at the maximum (with some TakeDamage(extra degrees) called in self as well).
    
    float BoneHealth -> If this is zero, will detach every attached bone before despawning, thus it is VERY important to set this!
    
    Class BoneActorEffectBSP -> The effect spawned if touching geometry.
    
    Class BoneActorEffectClasses[64]* -> The corresponding classes for each effect that should be spawned in BoneActorEffects.
    
    Class BoneActorEffects[64]* -> Like BoneActorSounds, but for spawned Actor effects.
    
    Class BoneActorEffectGeneric* -> Effect spawned if bounced/collided off a actor which class does not match any in BoneActorEffectClasses.
    
    name BoneActorEvent* -> Event fired when touching any Actor.
    
    name BoneBSPEvent* -> Event fired when collided with geometry.
    
    Rotator DisplayRotationOffset* -> The offset the display mesh will be offset by (in rotation), for fine tuning and adjusting.
    
    CaioOrganism Organism -> The organism it's part of, will determine lots of important things. Set it to the organism you spawned.
    
    bool bSetStartLocationToOwnerLocation -> Sets the start location to the Location of the Owner (must be a Actor, in this case the one that spawned the CaioBone) instead of Organism's DefaultBoneLocation.
    
    
    
    * This property is optional to fill.
    
  5. Set the CaioOrganism's (CaioBone TopBone) property to the first bone.

    CaioBones' StartLocation vertex is determined by the PreviousBone and the EndLocation is the median of the StartLocation of all NextBones. If the PreviousBone is None, sets the StartLocation to DefaultBoneLocation in Organism (CaioOrganism), or to the Location of the Owner if bSetStartLocationToOwnerLocation is true.

    After the CaioBones are spawned, assign each one a Mesh. When the CaioBones are rotated, they will also display rotated by DisplayRotationOffset, which means if your mesh isn't properly rotated, this can be fixed in the Bone(!) which means no fiddling around with complicated (unlike Blender :ironic: ) modellers more and more.
  6. In the CaioOrganism you spawned, call ProcessBoneTree. This will iterate through all NextBones of every Bone in this linked list, and set some important parameters.
  7. Enjoy!
This is how the bone's physics system works:
  • Each bone is set to PHYS_Falling.
  • If some bone ends up being rotated, offset the NextBones so that they are offset by the offset the EndPoint has traveled. And rotates them if necessary, accordingly.
  • If the PreviousBone has rotated in this tick, rotate and offset (all the NextBones as well) so that the StartPoint still matches PreviousBone's end point.
  • If the PreviousBone's offset this tick is different from this one's offset, move and rotate accordingly so start and end points matches. Repeat for EVERY SINGLE NextBone in this tree. (may lag a bit for gigantic bone trees)
Did you like my idea? It uses sin and cos for rotation! :ironic:
"Everyone is an idea man. Everybody thinks they have a revolutionary new game concept that no one else has ever thought of. Having cool ideas will rarely get you anywhere in the games industry. You have to be able to implement your ideas or provide some useful skill. Never join a project whose idea man or leader has no obvious development skills. Never join a project that only has a web designer. You have your own ideas. Focus on them carefully and in small chunks and you will be able to develop cool projects."

Weapon of Destruction
User avatar
FXANBSS
Skilled
Posts: 231
Joined: Thu Dec 26, 2013 7:03 pm

Re: Idea: Caio Engine, UScript physics in UE1

Post by FXANBSS »

what

EDIT: i hope this can do something useful.
Last edited by FXANBSS on Fri May 06, 2016 6:40 pm, edited 1 time in total.
User avatar
Barbie
Godlike
Posts: 2802
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Idea: Caio Engine, UScript physics in UE1

Post by Barbie »

Gustavo6046 wrote:make a nice revolution in UT
Because deriving the aim of this from the code is troublesome: can you explain what this is intended for and what it does? An example would be fine. :) And concerning TL;DR - two sentences should be enough.
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
User avatar
Gustavo6046
Godlike
Posts: 1462
Joined: Mon Jun 01, 2015 7:08 pm
Personal rank: Resident Wallaby
Location: Porto Alegre, Brazil
Contact:

Re: Idea: Caio Engine, UScript physics in UE1

Post by Gustavo6046 »

Basically, the code tag was only used for formatting purposes.

For a TL;DR, I'd say:
This is a basic physics system for Unreal Engine 1 that is similiar to Unreal Engine 2's Karma in many ways, while I don't know Karma in details, I know this system is a bit different, having CaioOrganism and such.

"Caio" is a quite popular forename and middle name in Brazil.
Also, this doesn't currently have much buoyancy/"air physics" coverage, so I'd like someone to help me fill in the rest about trajectory and joint physics.
"Everyone is an idea man. Everybody thinks they have a revolutionary new game concept that no one else has ever thought of. Having cool ideas will rarely get you anywhere in the games industry. You have to be able to implement your ideas or provide some useful skill. Never join a project whose idea man or leader has no obvious development skills. Never join a project that only has a web designer. You have your own ideas. Focus on them carefully and in small chunks and you will be able to develop cool projects."

Weapon of Destruction
User avatar
PrinceOfFunky
Godlike
Posts: 1200
Joined: Mon Aug 31, 2015 10:31 pm

Re: Idea: Caio Engine, UScript physics in UE1

Post by PrinceOfFunky »

I like the idea, this could be useful to make ragdolls and obviously improve the gameplay itself.

EDIT: What about CaioJunction? If you create junctions, it will be easier to determinate the position of the bones. Bones wouldn't need to have a collision, since a bone is a trace line between two junctions and cannot curve, so if one or both the junctions collide with something, it means the bone collided too.
Continue of the EDIT: If an actor pass through two junctions, the bone will "feel" it, because the trace line function gives you any actor the trace line collided with.
"Your stuff is known to be buggy and unfinished/not properly tested"
User avatar
Gustavo6046
Godlike
Posts: 1462
Joined: Mon Jun 01, 2015 7:08 pm
Personal rank: Resident Wallaby
Location: Porto Alegre, Brazil
Contact:

Re: Idea: Caio Engine, UScript physics in UE1

Post by Gustavo6046 »

PrinceOfFunky wrote:What about CaioJunction? If you create junctions, it will be easier to determinate the position of the bones. Bones wouldn't need to have a collision, since a bone is a trace line between two junctions and cannot curve, so if one or both the junctions collide with something, it means the bone collided too.
If an actor pass through two junctions, the bone will "feel" it, because the trace line function gives you any actor the trace line collided with.
Nice idea! But junctions would be harder to code mesh-wise. I don't even know how would I display a mesh per junction!
"Everyone is an idea man. Everybody thinks they have a revolutionary new game concept that no one else has ever thought of. Having cool ideas will rarely get you anywhere in the games industry. You have to be able to implement your ideas or provide some useful skill. Never join a project whose idea man or leader has no obvious development skills. Never join a project that only has a web designer. You have your own ideas. Focus on them carefully and in small chunks and you will be able to develop cool projects."

Weapon of Destruction
User avatar
PrinceOfFunky
Godlike
Posts: 1200
Joined: Mon Aug 31, 2015 10:31 pm

Re: Idea: Caio Engine, UScript physics in UE1

Post by PrinceOfFunky »

Gustavo6046 wrote:Nice idea! But junctions would be harder to code mesh-wise. I don't even know how would I display a mesh per junction!
If you only use meshes, they will always have a cylindrical collision that will never rotate, so maybe you could use junctions to check collisions and fit meshes without collisions between junctions like bones and rotate them.
"Your stuff is known to be buggy and unfinished/not properly tested"
User avatar
Gustavo6046
Godlike
Posts: 1462
Joined: Mon Jun 01, 2015 7:08 pm
Personal rank: Resident Wallaby
Location: Porto Alegre, Brazil
Contact:

Re: Idea: Caio Engine, UScript physics in UE1

Post by Gustavo6046 »

PrinceOfFunky wrote: If you only use meshes, they will always have a cylindrical collision that will never rotate, so maybe you could use junctions to check collisions and fit meshes without collisions between junctions like bones and rotate them.
Wow, it's true! You just reminded me collisions *don't* rotate.

It'll still need a trace per junction per tick!

However, I ended up coding a stub:

Code: Select all

// |CaioJunction|
//
// A alternative to CaioBone. THIS IS A STUB! Does not have much physics code yet.
//
// A junction is a spot where a mesh protrudes from, that can add ragdoll physics
// to any meshes.
//
// Usually coordinate by a main CaioOrganism.

class CaioJunction extends Actor;

var CaioJunction NextJunctions[16];
var CaioJunction PreviousJunction;
var Sound ActorCollisionSounds[32];
var Class<Actor> ActorCollisionClasses[32];
var float JunctionFlexibility;
var Rotator MeshOffsetRotation;
var Sound GeometryCollisionSound;
var float JunctionHealth;
//===
// Next 3 variables:
// AC = At Actor
// AJ = At Junction
var Class<Actor> ActorCollisionEffectAA;
var Class<Actor> ActorCollisionEffectAJ;
var Class<Actor> GeometryCollisionEffectAJ;
//===
var Sound GenericBSPCollisionSound;
var Rotator DisplayRotationOffset;
var enum EJunctionLocationType
{
	JLT_AtOwner,
	JLT_AtOrganism,
	JLT_AtPreviousEndPoint,
	JLT_AtSpawnLocation
} JunctionLocationType;
var CaioOrganism MainOrganism;
var Vector EndPoint;
var float Radius;
var float Buoyancy;

static function NewVector(float X, float Y, float Z)
{
	local Vector V;
	
	V.X = X;
	V.Y = Y;
	V.Z = Z;
	
	return V;
}

function SetJunctionProperties(CaioOrganism Organism, CaioJunction PreviousJunction, CaioJunction NextJunctions, optional float JunctionHealth, optional EJunctionLocationType LocationType, optional Class ActorCollisionEffectAA, optional Class ActorCollisionEffectAJ, optional Class GeometryCollisionEffectAJ, optional Sound GenericCollisionSound, optional Sound GeometryCollisionSound, optional Class ActorCollisionClasses[32], optional Sound ActorCollisionSounds[32], optional float Flexibility, optional float Buoyancy)
{
	local EPV;
	local int i;
	
	self.PreviousJunction = PreviousJunction;
	self.NextJunctions = NextJunctions;
	self.JunctionHealth = JunctionHealth;
	JunctionLocationType = LocationType;
	self.ActorCollisionEffectAA = ActorCollisionEffectAA;
	self.ActorCollisionEffectAJ = ActorCollisionEffectAJ;
	self.GeometryCollisionEffectAJ = GeometryCollisionEffectAJ;
	self.GenericCollisionSound = GenericCollisionSound;
	self.GeometryCollisionSound = GeometryCollisionSound;
	self.ActorCollisionClasses = ActorCollisionClasses;
	self.ActorCollisionSounds = ActorCollisionSounds;
	self.Flexibility = Flexibility;
	self.Buoyancy = Buoyancy;
	self.MainOrganism = Organism;
	
	if ( JunctionLocationType == JLT_AtOwner )
		SetLocation(Owner.Location);
		
	else if ( JunctionLocationType == JLT_AtOrganism )
		SetLocation(MainOrganism.Location);
		
	else if ( JunctionLocationType == JLT_AtPreviousEndPoint )
		GoToState('Locating');
	
	EPV = static.NewVector(0, 0, 0);
	
	for ( i = 0; i < 16; i++ )
		if ( NextJunctions[i] != None )
			EPV += NextJunctions[i].Location;
	
	self.EndPoint = EPV / 16;
	
	if ( !IsInState('Locating') )
		GoToState('ActivatedJunction');
}

auto state() DeactivatedJunction
{
}

state Locating
{
	Begin:
		while ( PreviousJunction == None || PreviousJunction.EndPoint == vect(0,0,0) )
			sleep(0.0);
			
		SetLocation(PreviousJunction.EndPoint);
		GoToState('ActivatedJunction');
}

function IterateActorReflect(Actor Other, Vector HitNormal, optional bool bTop)
{
	local int i;
	local bool bFoundClass;
	
	for ( i = 0; i < 16; i++ )
		if ( NextJunctions[i] != None )
			NextJunctions[i].IterateActorReflect(Other, HitNormal, true);
	
	for ( i = 0; i < 32; i++ )
		if ( Actor.class == ActorCollisionClasses[i] && ActorCollisionSounds[i] != None && !bTop )
		{
			PlaySound(ActorCollisionSounds[i]);
			bFoundClass = True;
		}
		
	if ( !bFoundClass )
		PlaySound(GenericCollisionSound);
	
	if ( !bTop && ActorCollisionEffectAJ != None )
		Spawn(ActorCollisionEffectAJ, self);
		
	if ( !bTop && ActorCollisionEffectAA != None )
		Spawn(ActorCollisionEffectAA, self, ActorCollisionEffectAA.Name, Actor.Location, Actor.Rotation)
	
	Velocity += HitNormal * (VSize(Velocity) * Buoyancy);
}

function IterateWallReflect(Vector HitNormal, optional bool bTop)
{
	for ( i = 0; i < 16; i++ )
		if ( NextJunctions[i] != None )
			NextJunctions[i].IterateWallReflect(HitNormal, true);

	if ( !bTop && GeometryCollisionSound != None )
		PlaySound(GeometryCollisionSound)
		
	if ( !bTop && GeometryCollisionEffectAJ != None )
		Spawn(GeometryCollisionEffectAJ, self);
			
	Velocity += HitNormal * (VSize(Velocity) * Buoyancy);
}

state ActivatedJunction
{
	function Tick(float TimeDelta)
	{
		local int i;
		local Actor CollidedActor;
		local Vector ActorNormal;
		
		CollidedActor = Trace(vect(0,0,0), ActorNormal, EndPoint, Location);
		
		if ( CollidedActor == None )
			return;
		
		else if ( CollidedActor.Name == 'LevelInfo0' )
		{
			for ( i = 0; i < 16; i++ )
				if ( NextJunctions[i] != None )
					NextJunctions[i].IterateWallReflect(ActorNormal);
		}
		
		else
		{
			if ( CollidedActor.bBlockActors )
			{
				CollidedActor.Bump(self);
				Bump(CollidedActor);
				
				for ( i = 0; i < 16; i++ )
					if ( NextJunctions[i] != None )
						NextJunctions[i].Bump(CollidedActor);
			}
				
			else
			{
				CollidedActor.Touch(self);
				Touch(CollidedActor);
				
				for ( i = 0; i < 16; i++ )
					if ( NextJunctions[i] != None )
						NextJunctions[i].Touch(CollidedActor);
			}
			
			for ( i = 0; i < 16; i++ )
				if ( NextJunctions[i] != None )
					NextJunctions[i].IterateActorReflect(CollidedActor, ActorNormal);
		}
	}
	
	function Landed(vector HitNormal)
	{
		IterateWallReflect(HitNormal);
	}
}

Code: Select all

// |CaioOrganism|
//
// An way of organizing a tree of CaioJunction's.

class CaioOrganism extends Actor;

//stub
Thoughs?

CaioJunction (line 60) says I'M TRYING TO ASSIGN BOOLEAN ARRAYS! DOES THAT MAKES SENSE ???
"Everyone is an idea man. Everybody thinks they have a revolutionary new game concept that no one else has ever thought of. Having cool ideas will rarely get you anywhere in the games industry. You have to be able to implement your ideas or provide some useful skill. Never join a project whose idea man or leader has no obvious development skills. Never join a project that only has a web designer. You have your own ideas. Focus on them carefully and in small chunks and you will be able to develop cool projects."

Weapon of Destruction
User avatar
Gustavo6046
Godlike
Posts: 1462
Joined: Mon Jun 01, 2015 7:08 pm
Personal rank: Resident Wallaby
Location: Porto Alegre, Brazil
Contact:

Re: Idea: Caio Engine, UScript physics in UE1

Post by Gustavo6046 »

I'm still stuck trying to make the "actor-array-that-the-compiler-thinks-is-a-boolean-array" work! Any ideas why doesn't it work???

BTW, I finished coding the rather simple CaioOrganism:

Code: Select all

// |CaioOrganism|
//
// An way of organizing a tree of CaioJunction's.

class CaioOrganism extends Actor;

var CaioJunction TopJunction;
var float AccumulatedTime;
var int Attempts;

function Timer()
{
	GoToState('HasNoTopJunction');
}

state() SearchingTopJunction
{
	function PostBeginPlay()
	{
		SetTimer(30, false);
	}

	function Tick(float TimeDelta)
	{
		local CaioJunction CJ;
	
		AccumulatedTime += TimeDelta;
		
		if ( AccumulatedTime > Attempts * 5 )
			foreach AllActors(class'CaioJunction', CJ)
				if ( CJ.MainOrganism == self && CJ.PreviousJunction == None )
				{
					TopJunction = CJ;
					GoToState('HasTopJunction');
				}
	}
}

state() HasNoTopJunction {}
state HasTopJunction {}
EDIT: I did get around it with a automatic detection of NextJunctions in a basis: every CaioJunction with PreviousJunction == Self is a NextJunction. I did also clean up the argument list at SetJunctionProperties, and it looks WAY better, as well as some awesome fixes.. and now IT COMPILES:

Code: Select all

// |CaioJunction|
//
// A alternative to CaioBone. THIS IS A STUB! Does not have much physics code yet.
//
// A junction is a spot where a mesh protrudes from, that can add ragdoll physics
// to any meshes.
//
// Usually coordinate by a main CaioOrganism.

class CaioJunction extends Actor;

var CaioJunction NextJunctions[32];
var CaioJunction PreviousJunction;
var Sound ActorCollisionSounds[32];
var Sound GenericCollisionSound;
var Class<Actor> ActorCollisionClasses[32];
var float JunctionFlexibility;
var Rotator MeshOffsetRotation;
var Sound GeometryCollisionSound;
var float JunctionHealth;
//===
// Next 3 variables:
// AC = At Actor
// AJ = At Junction
var Class<Actor> ActorCollisionEffectAA;
var Class<Actor> ActorCollisionEffectAJ;
var Class<Actor> GeometryCollisionEffectAJ;
//===
var Sound GenericBSPCollisionSound;
var Rotator DisplayRotationOffset;
var enum EJunctionLocationType
{
	JLT_AtOwner,
	JLT_AtOrganism,
	JLT_AtPreviousEndPoint,
	JLT_AtSpawnLocation
} JunctionLocationType;
var CaioOrganism MainOrganism;
var Vector EndPoint;
var float Buoyancy;

static function vector NewVector(float X, float Y, float Z)
{
	local Vector V;
	
	V.X = X;
	V.Y = Y;
	V.Z = Z;
	
	return V;
}

function PreBeginPlay()
{
	local int i;
	local CaioJunction CJ;
	local bool bSet;

	foreach AllActors(class'CaioJunction', CJ)
		if ( CJ.PreviousJunction == self )
		{
			bSet = False;
			
			for ( i = 0; i < 32; i++ )
				if ( NextJunctions[i] == None )
				{
					NextJunctions[i] = CJ;
					bSet = True;
					break;
				}
				
			if ( !bSet )
				Warn("Warning:" @ name $ "'s amount of NextJunctions exceed 32! Other CaioJunctions will be ignored and NOT set as NextJunctions in" @ name $ "!");
		}
}

function SetJunctionProperties(
	CaioOrganism Organism,
	CaioJunction PreviousJunction,
	optional float JunctionHealth,
	optional EJunctionLocationType LocationType,
	optional Class<Actor> ActorCollisionEffectAA,
	optional Class<Actor> ActorCollisionEffectAJ,
	optional Class<Actor> GeometryCollisionEffectAJ,
	optional Sound GenericCollisionSound,
	optional Sound GeometryCollisionSound,
	optional Class<Actor> ActorCollisionClasses[32],
	optional Sound ActorCollisionSounds[32],
	optional float Buoyancy
)
{
	local vector EPV;
	local int i;
	
	self.PreviousJunction = PreviousJunction;
	self.JunctionHealth = JunctionHealth;
	JunctionLocationType = LocationType;
	self.ActorCollisionEffectAA = ActorCollisionEffectAA;
	self.ActorCollisionEffectAJ = ActorCollisionEffectAJ;
	self.GeometryCollisionEffectAJ = GeometryCollisionEffectAJ;
	self.GenericCollisionSound = GenericCollisionSound;
	self.GeometryCollisionSound = GeometryCollisionSound;
	self.Buoyancy = Buoyancy;
	self.MainOrganism = Organism;
	
	for ( i = 0; i < 32; i++ )
	{
		self.ActorCollisionClasses[i] = ActorCollisionClasses[i];
		self.ActorCollisionSounds[i] = ActorCollisionSounds[i];
	}
	
	if ( JunctionLocationType == JLT_AtOwner )
		SetLocation(Owner.Location);
		
	else if ( JunctionLocationType == JLT_AtOrganism )
		SetLocation(MainOrganism.Location);
		
	else if ( JunctionLocationType == JLT_AtPreviousEndPoint )
		GoToState('Locating');
	
	for ( i = 0; i < 16; i++ )
		if ( NextJunctions[i] != None )
			EPV += NextJunctions[i].Location;
	
	self.EndPoint = EPV / 16;
	
	if ( !IsInState('Locating') )
		GoToState('ActivatedJunction');
}

auto state() DeactivatedJunction
{
}

state Locating
{
	Begin:
		while ( PreviousJunction == None || PreviousJunction.EndPoint == vect(0,0,0) )
			sleep(0.0);
			
		SetLocation(PreviousJunction.EndPoint);
		GoToState('ActivatedJunction');
}

function IterateActorReflect(Actor Other, Vector HitNormal, optional bool bTop)
{
	local int i;
	local bool bFoundClass;
	
	for ( i = 0; i < 16; i++ )
		if ( NextJunctions[i] != None )
			NextJunctions[i].IterateActorReflect(Other, HitNormal, true);
	
	for ( i = 0; i < 32; i++ )
		if ( Other.class == ActorCollisionClasses[i] && ActorCollisionSounds[i] != None && !bTop )
		{
			PlaySound(ActorCollisionSounds[i]);
			bFoundClass = True;
		}
		
	if ( !bFoundClass )
		PlaySound(GenericCollisionSound);
	
	if ( !bTop && ActorCollisionEffectAJ != None )
		Spawn(ActorCollisionEffectAJ, self);
		
	if ( !bTop && ActorCollisionEffectAA != None )
		Spawn(ActorCollisionEffectAA, self, ActorCollisionEffectAA.Name, Other.Location, Other.Rotation);
	
	Velocity += HitNormal * (VSize(Velocity) * Buoyancy);
}

function IterateWallReflect(Vector HitNormal, optional bool bTop)
{
	local int i;

	for ( i = 0; i < 16; i++ )
		if ( NextJunctions[i] != None )
			NextJunctions[i].IterateWallReflect(HitNormal, true);

	if ( !bTop && GeometryCollisionSound != None )
		PlaySound(GeometryCollisionSound);
		
	if ( !bTop && GeometryCollisionEffectAJ != None )
		Spawn(GeometryCollisionEffectAJ, self);
			
	Velocity += HitNormal * (VSize(Velocity) * Buoyancy);
}

state ActivatedJunction
{
	function Tick(float TimeDelta)
	{
		local int i;
		local Actor CollidedActor;
		local Vector ActorNormal;
		local Vector Unused;
		
		CollidedActor = Trace(Unused, ActorNormal, EndPoint, Location);
		
		if ( CollidedActor == None )
			return;
		
		else if ( CollidedActor.Name == 'LevelInfo0' )
		{
			for ( i = 0; i < 16; i++ )
				if ( NextJunctions[i] != None )
					NextJunctions[i].IterateWallReflect(ActorNormal);
		}
		
		else
		{
			if ( CollidedActor.bBlockActors )
			{
				CollidedActor.Bump(self);
				Bump(CollidedActor);
				
				for ( i = 0; i < 16; i++ )
					if ( NextJunctions[i] != None )
						NextJunctions[i].Bump(CollidedActor);
			}
				
			else
			{
				CollidedActor.Touch(self);
				Touch(CollidedActor);
				
				for ( i = 0; i < 16; i++ )
					if ( NextJunctions[i] != None )
						NextJunctions[i].Touch(CollidedActor);
			}
			
			for ( i = 0; i < 16; i++ )
				if ( NextJunctions[i] != None )
					NextJunctions[i].IterateActorReflect(CollidedActor, ActorNormal);
		}
	}
	
	function Landed(vector HitNormal)
	{
		IterateWallReflect(HitNormal);
	}
}
All that remains now is to make ONE more class, that is spawned between the junction and it's PreviousJunction, that somehow can rotate accordingly to the offsets of the junctions it's in between!

EDIT 2: To avoid complicating things, I decided instead that the mesh will be displayed by the junction itself. Keep in mind the mesh, however, needs to have it's origin at it's back edge for it to be displayed properly!

So, I finished the BASIC LEVEL physics code:

Code: Select all

// |CaioJunction|
//
// A alternative to CaioBone. THIS IS A STUB! Does not have much physics code yet.
//
// A junction is a spot where a mesh protrudes from, that can add ragdoll physics
// to any meshes.
//
// Usually coordinate by a main CaioOrganism.

class CaioJunction extends Actor;

var bool bDamaging;
var CaioJunction NextJunctions[32];
var CaioJunction PreviousJunction;
var Sound ActorCollisionSounds[32];
var Sound GenericCollisionSound;
var Class<Actor> ActorCollisionClasses[32];
var float JunctionFlexibility;
var Rotator MeshOffsetRotation;
var Sound GeometryCollisionSound;
var float JunctionHealth;
//===
// Next 3 variables:
// AC = At Actor
// AJ = At Junction
var Class<Actor> ActorCollisionEffectAA;
var Class<Actor> ActorCollisionEffectAJ;
var Class<Actor> GeometryCollisionEffectAJ;
//===
var Sound GenericBSPCollisionSound;
var Rotator DisplayRotationOffset;
var enum EJunctionLocationType
{
	JLT_AtOwner,
	JLT_AtOrganism,
	JLT_AtPreviousEndPoint,
	JLT_AtSpawnLocation
} JunctionLocationType;
var CaioOrganism MainOrganism;
var Vector EndPoint;
var float Buoyancy;
var float Lenght;

static function vector NewVector(float X, float Y, float Z)
{
	local Vector V;
	
	V.X = X;
	V.Y = Y;
	V.Z = Z;
	
	return V;
}

function PreBeginPlay()
{
	local int i;
	local CaioJunction CJ;
	local bool bSet;

	foreach AllActors(class'CaioJunction', CJ)
		if ( CJ.PreviousJunction == self )
		{
			bSet = False;
			
			for ( i = 0; i < 32; i++ )
				if ( NextJunctions[i] == None )
				{
					NextJunctions[i] = CJ;
					bSet = True;
					break;
				}
				
			if ( !bSet )
				Warn("Warning:" @ name $ "'s amount of NextJunctions exceed 32! Other CaioJunctions will be ignored and NOT set as NextJunctions in" @ name $ "!");
		}
}

function SetJunctionProperties(
	CaioOrganism Organism,
	CaioJunction PreviousJunction,
	float Lenght,
	optional float JunctionHealth,
	optional EJunctionLocationType LocationType,
	optional Class<Actor> ActorCollisionEffectAA,
	optional Class<Actor> ActorCollisionEffectAJ,
	optional Class<Actor> GeometryCollisionEffectAJ,
	optional Sound GenericCollisionSound,
	optional Sound GeometryCollisionSound,
	optional Class<Actor> ActorCollisionClasses[32],
	optional Sound ActorCollisionSounds[32],
	optional float Buoyancy
)
{
	local int i;
	
	self.PreviousJunction = PreviousJunction;
	self.JunctionHealth = JunctionHealth;
	JunctionLocationType = LocationType;
	self.ActorCollisionEffectAA = ActorCollisionEffectAA;
	self.ActorCollisionEffectAJ = ActorCollisionEffectAJ;
	self.GeometryCollisionEffectAJ = GeometryCollisionEffectAJ;
	self.GenericCollisionSound = GenericCollisionSound;
	self.GeometryCollisionSound = GeometryCollisionSound;
	self.Buoyancy = Buoyancy;
	self.MainOrganism = Organism;
	self.Lenght = Lenght;
	
	if ( JunctionHealth == 0 )
		bDamaging = False;
		
	else
		bDamaging = True;
	
	for ( i = 0; i < 32; i++ )
	{
		self.ActorCollisionClasses[i] = ActorCollisionClasses[i];
		self.ActorCollisionSounds[i] = ActorCollisionSounds[i];
	}
	
	if ( JunctionLocationType == JLT_AtOwner )
		SetLocation(Owner.Location);
		
	else if ( JunctionLocationType == JLT_AtOrganism )
		SetLocation(MainOrganism.Location);
		
	else if ( JunctionLocationType == JLT_AtPreviousEndPoint )
		GoToState('Locating');
	
	if ( !IsInState('Locating') )
		GoToState('ActivatedJunction');
}

auto state() DeactivatedJunction
{
}

state Locating
{
	Begin:
		while ( PreviousJunction == None || PreviousJunction.EndPoint == vect(0,0,0) )
			sleep(0.0);
			
		SetLocation(PreviousJunction.EndPoint);
		GoToState('ActivatedJunction');
}

function IterateActorReflect(Actor Other, Vector HitNormal, optional bool bTop)
{
	local int i;
	local bool bFoundClass;
	
	for ( i = 0; i < 16; i++ )
		if ( NextJunctions[i] != None )
			NextJunctions[i].IterateActorReflect(Other, HitNormal, true);
	
	for ( i = 0; i < 32; i++ )
		if ( Other.class == ActorCollisionClasses[i] && ActorCollisionSounds[i] != None && !bTop )
		{
			PlaySound(ActorCollisionSounds[i]);
			bFoundClass = True;
		}
		
	if ( !bFoundClass )
		PlaySound(GenericCollisionSound);
	
	if ( !bTop && ActorCollisionEffectAJ != None )
		Spawn(ActorCollisionEffectAJ, self);
		
	if ( !bTop && ActorCollisionEffectAA != None )
		Spawn(ActorCollisionEffectAA, self, ActorCollisionEffectAA.Name, Other.Location, Other.Rotation);
	
	Velocity += HitNormal * (VSize(Velocity) * Buoyancy);
}

function IterateWallReflect(Vector HitNormal, optional bool bTop)
{
	local int i;

	for ( i = 0; i < 16; i++ )
		if ( NextJunctions[i] != None )
			NextJunctions[i].IterateWallReflect(HitNormal, true);

	if ( !bTop && GeometryCollisionSound != None )
		PlaySound(GeometryCollisionSound);
		
	if ( !bTop && GeometryCollisionEffectAJ != None )
		Spawn(GeometryCollisionEffectAJ, self);
			
	Velocity += HitNormal * (VSize(Velocity) * Buoyancy);
}

state ActivatedJunction
{
	function TakeDamage(int Damage, Pawn EventInstigator, vector HitLocation, vector Momentum, name DamageType)
	{
		local int i;
		local int i2;
	
		if ( bDamaging )
		{
			JunctionHealth -= Damage;
			
			if ( JunctionHealth <= 0 )
			{
				for ( i = 0; i < 32; i++ )
				{
					if ( NextJunctions[i] != None )
						NextJunctions[i].PreviousJunction = None;
						
					if ( PreviousJunction.NextJunctions[i] == self )
						for ( i2 = i; i2 < 31; i2++ )
						{
							if ( PreviousJunction.NextJunctions[i2 + 1] == None )
								break;
						
							PreviousJunction.NextJunctions[i2] = PreviousJunction.NextJunctions[i2 + 1];
						}
				}
			
				Destroy();
			}
		}
	}

	function Tick(float TimeDelta)
	{
		local int i;
		local Actor CollidedActor;
		local Vector ActorNormal;
		local Vector Unused;
		local Vector EPV;
		
		CollidedActor = Trace(Unused, ActorNormal, EndPoint, Location);
		
		if ( CollidedActor == None )
			return;
		
		else if ( CollidedActor.Name == 'LevelInfo0' )
		{
			for ( i = 0; i < 32; i++ )
				if ( NextJunctions[i] != None )
					NextJunctions[i].IterateWallReflect(ActorNormal);
		}
		
		else
		{
			if ( CollidedActor.bBlockActors )
			{
				CollidedActor.Bump(self);
				Bump(CollidedActor);
				
				for ( i = 0; i < 32; i++ )
					if ( NextJunctions[i] != None )
						NextJunctions[i].Bump(CollidedActor);
			}
				
			else
			{
				CollidedActor.Touch(self);
				Touch(CollidedActor);
				
				for ( i = 0; i < 32; i++ )
					if ( NextJunctions[i] != None )
						NextJunctions[i].Touch(CollidedActor);
			}
			
			for ( i = 0; i < 32; i++ )
				if ( NextJunctions[i] != None )
					NextJunctions[i].IterateActorReflect(CollidedActor, ActorNormal);
		}
		
		for ( i = 0; i < 32; i++ )		
			if ( VSize(NextJunctions[i].Location - Location) != Lenght )
				MoveSmooth(Normal(NextJunctions[i].Location - Location) * Lenght);
		
		for ( i = 0; i < 32; i++ )
			if ( NextJunctions[i] != None )
				EPV += NextJunctions[i].Location;
	
		EndPoint = EPV / 32;
	}
	
	function Landed(vector HitNormal)
	{
		IterateWallReflect(HitNormal);
	}
}
I don't have meshes to test this, so could someone lend a mesh with it's origin at a edge?

EDIT 3: Now you can place custom ragdolls in maps! :D

Code: Select all

// |CaioJunction|
//
// A alternative to CaioBone. THIS IS A STUB! Does not have much physics code yet.
//
// A junction is a spot where a mesh protrudes from, that can add ragdoll physics
// to any meshes.
//
// Usually coordinate by a main CaioOrganism.

class CaioJunction extends Actor;

var(Junction) bool bMapJunction;
var(Junction) name PreviousJunctionName;
var(Junction) bool bShootable;
var CaioJunction NextJunctions[32];
var CaioJunction PreviousJunction;
var(Junction) Sound ActorCollisionSounds[32];
var(Junction) Sound GenericCollisionSound;
var(Junction) Class<Actor> ActorCollisionClasses[32];
var(Junction) Sound GeometryCollisionSound;
var(Junction) float JunctionHealth;
//===
// Next 3 variables:
// AC = At Actor
// AJ = At Junction
var(Junction) Class<Actor> ActorCollisionEffectAA;
var(Junction) Class<Actor> ActorCollisionEffectAJ;
var(Junction) Class<Actor> GeometryCollisionEffectAJ;
//===
var(Junction) Sound GenericBSPCollisionSound;
var(Junction) Rotator DisplayRotationOffset;
var(Junction) enum EJunctionLocationType
{
	JLT_AtOwner,
	JLT_AtOrganism,
	JLT_AtPreviousEndPoint,
	JLT_AtSpawnLocation
} JunctionLocationType;
var(Junction) name MainOrganismName;
var CaioOrganism MainOrganism;
var Vector EndPoint;
var(Junction) float Buoyancy;
var(Junction) float Lenght;

static function vector NewVector(float X, float Y, float Z)
{
	local Vector V;
	
	V.X = X;
	V.Y = Y;
	V.Z = Z;
	
	return V;
}

function PreBeginPlay()
{
	local CaioJunction CJ;
	local CaioOrganism BS;
	
	foreach AllActors(class'CaioJunction', CJ)
	{
		if ( bMapJunction && CJ.Name == PreviousJunctionName )
			PreviousJunction = CJ;
	}
	
	foreach AllActors(class'CaioOrganism', BS)
	{
		if ( bMapJunction && BS.Name == MainOrganismName )
			MainOrganism = BS;
	}
}

function BeginPlay()
{
	local int i;
	local CaioJunction CJ;
	local bool bSet;

	foreach AllActors(class'CaioJunction', CJ)
	{
		if ( CJ.PreviousJunction == self )
			{
				bSet = False;
				
				for ( i = 0; i < 32; i++ )
					if ( NextJunctions[i] == None )
					{
						NextJunctions[i] = CJ;
						bSet = True;
						break;
					}
					
				if ( !bSet )
					Warn("One of" @ name $ "'s NextJunctions wasn't set! Decrease the amount of Junctions that have this Junction as PreviousJunction to lower than 33 or contact me if this doesn't solves!");
			}
	}
}

function SetJunctionProperties(
	CaioOrganism Organism,
	CaioJunction PreviousJunction,
	float Lenght,
	optional float JunctionHealth,
	optional EJunctionLocationType LocationType,
	optional Class<Actor> ActorCollisionEffectAA,
	optional Class<Actor> ActorCollisionEffectAJ,
	optional Class<Actor> GeometryCollisionEffectAJ,
	optional Sound GenericCollisionSound,
	optional Sound GeometryCollisionSound,
	optional Class<Actor> ActorCollisionClasses[32],
	optional Sound ActorCollisionSounds[32],
	optional float Buoyancy
)
{
	local int i;
	
	self.PreviousJunction = PreviousJunction;
	self.JunctionHealth = JunctionHealth;
	JunctionLocationType = LocationType;
	self.ActorCollisionEffectAA = ActorCollisionEffectAA;
	self.ActorCollisionEffectAJ = ActorCollisionEffectAJ;
	self.GeometryCollisionEffectAJ = GeometryCollisionEffectAJ;
	self.GenericCollisionSound = GenericCollisionSound;
	self.GeometryCollisionSound = GeometryCollisionSound;
	self.Buoyancy = Buoyancy;
	self.MainOrganism = Organism;
	self.Lenght = Lenght;
	
	if ( JunctionHealth == 0 )
		bShootable = False;
		
	else
		bShootable = True;
	
	for ( i = 0; i < 32; i++ )
	{
		self.ActorCollisionClasses[i] = ActorCollisionClasses[i];
		self.ActorCollisionSounds[i] = ActorCollisionSounds[i];
	}
	
	if ( JunctionLocationType == JLT_AtOwner )
		SetLocation(Owner.Location);
		
	else if ( JunctionLocationType == JLT_AtOrganism )
		SetLocation(MainOrganism.Location);
		
	else if ( JunctionLocationType == JLT_AtPreviousEndPoint )
		GoToState('Locating');
	
	if ( !IsInState('Locating') )
		GoToState('ActivatedJunction');
}

auto state() DeactivatedJunction
{
}

state Locating
{
	Begin:
		while ( PreviousJunction == None || PreviousJunction.EndPoint == vect(0,0,0) )
			sleep(0.0);
			
		SetLocation(PreviousJunction.EndPoint);
		GoToState('ActivatedJunction');
}

function IterateActorReflect(Actor Other, Vector HitNormal, optional bool bTop)
{
	local int i;
	local bool bFoundClass;
	
	for ( i = 0; i < 16; i++ )
		if ( NextJunctions[i] != None )
			NextJunctions[i].IterateActorReflect(Other, HitNormal, true);
	
	for ( i = 0; i < 32; i++ )
		if ( Other.class == ActorCollisionClasses[i] && ActorCollisionSounds[i] != None && !bTop )
		{
			PlaySound(ActorCollisionSounds[i]);
			bFoundClass = True;
		}
		
	if ( !bFoundClass )
		PlaySound(GenericCollisionSound);
	
	if ( !bTop && ActorCollisionEffectAJ != None )
		Spawn(ActorCollisionEffectAJ, self);
		
	if ( !bTop && ActorCollisionEffectAA != None )
		Spawn(ActorCollisionEffectAA, self, ActorCollisionEffectAA.Name, Other.Location, Other.Rotation);
	
	Velocity += HitNormal * (VSize(Velocity) * Buoyancy);
}

function IterateWallReflect(Vector HitNormal, optional bool bTop)
{
	local int i;

	for ( i = 0; i < 16; i++ )
		if ( NextJunctions[i] != None )
			NextJunctions[i].IterateWallReflect(HitNormal, true);

	if ( !bTop && GeometryCollisionSound != None )
		PlaySound(GeometryCollisionSound);
		
	if ( !bTop && GeometryCollisionEffectAJ != None )
		Spawn(GeometryCollisionEffectAJ, self);
			
	Velocity += HitNormal * (VSize(Velocity) * Buoyancy);
}

state ActivatedJunction
{
	function TakeDamage(int Damage, Pawn EventInstigator, vector HitLocation, vector Momentum, name DamageType)
	{
		local int i;
		local int i2;
	
		if ( bShootable )
		{
			JunctionHealth -= Damage;
			
			if ( JunctionHealth <= 0 )
			{
				for ( i = 0; i < 32; i++ )
				{
					if ( NextJunctions[i] != None )
						NextJunctions[i].PreviousJunction = None;
						
					if ( PreviousJunction.NextJunctions[i] == self )
						for ( i2 = i; i2 < 31; i2++ )
						{
							if ( PreviousJunction.NextJunctions[i2 + 1] == None )
								break;
						
							PreviousJunction.NextJunctions[i2] = PreviousJunction.NextJunctions[i2 + 1];
						}
				}
			
				Destroy();
			}
		}
	}

	function Tick(float TimeDelta)
	{
		local int i;
		local Actor CollidedActor;
		local Vector ActorNormal;
		local Vector Unused;
		local Vector EPV;
		
		CollidedActor = Trace(Unused, ActorNormal, EndPoint, Location);
		
		if ( CollidedActor == None )
			return;
		
		else if ( CollidedActor.Name == 'LevelInfo0' )
		{
			for ( i = 0; i < 32; i++ )
				if ( NextJunctions[i] != None )
					NextJunctions[i].IterateWallReflect(ActorNormal);
		}
		
		else
		{
			if ( CollidedActor.bBlockActors )
			{
				CollidedActor.Bump(self);
				Bump(CollidedActor);
				
				for ( i = 0; i < 32; i++ )
					if ( NextJunctions[i] != None )
						NextJunctions[i].Bump(CollidedActor);
			}
				
			else
			{
				CollidedActor.Touch(self);
				Touch(CollidedActor);
				
				for ( i = 0; i < 32; i++ )
					if ( NextJunctions[i] != None )
						NextJunctions[i].Touch(CollidedActor);
			}
			
			for ( i = 0; i < 32; i++ )
				if ( NextJunctions[i] != None )
					NextJunctions[i].IterateActorReflect(CollidedActor, ActorNormal);
		}
		
		for ( i = 0; i < 32; i++ )		
			if ( VSize(NextJunctions[i].Location - Location) != Lenght )
				MoveSmooth(Normal(NextJunctions[i].Location - Location) * Lenght);
		
		for ( i = 0; i < 32; i++ )
			if ( NextJunctions[i] != None )
				EPV += NextJunctions[i].Location;
	
		EndPoint = EPV / 32;
	}
	
	function Landed(vector HitNormal)
	{
		IterateWallReflect(HitNormal);
	}
}
Just make sure NOT to edit the default value of them! :P

EDIT 4: Look at the video:
Demo Video
R-LNaS_G-Mk
These rocket launchers are Junctions with Rocket Launcher mesh.

IT FAILSSSSSSSSSSS
WHY ???? ;_;
"Everyone is an idea man. Everybody thinks they have a revolutionary new game concept that no one else has ever thought of. Having cool ideas will rarely get you anywhere in the games industry. You have to be able to implement your ideas or provide some useful skill. Never join a project whose idea man or leader has no obvious development skills. Never join a project that only has a web designer. You have your own ideas. Focus on them carefully and in small chunks and you will be able to develop cool projects."

Weapon of Destruction
User avatar
PrinceOfFunky
Godlike
Posts: 1200
Joined: Mon Aug 31, 2015 10:31 pm

Re: Idea: Caio Engine, UScript physics in UE1

Post by PrinceOfFunky »

Well, comment the functions and the states and put some logs to find the mess.
My questions are, why do junctions need meshes? And why, if the expression {junction.previous == self} is true, then it means the junction is a "next" junction? Next to what?
"Your stuff is known to be buggy and unfinished/not properly tested"
JackGriffin
Godlike
Posts: 3774
Joined: Fri Jan 14, 2011 1:53 pm
Personal rank: -Retired-

Re: Idea: Caio Engine, UScript physics in UE1

Post by JackGriffin »

I'd junk this entire idea. It's not because it's not a good idea, it's just that a ton of the physics of actors is baked into the native code. You'll never get ragdoll unless you hide the player and create this whole new effect class with models, etc. It will never be the player though.

Think of it like this: You can't have a true spider physics in Unreal/UT because of the primitive nature of the engine. In essence you are limited to shoot-bumping like the pylon or over-riding the velocity of the actor.

Ferali really wanted to do this too with vehicles and gave up. So did dots with his version of physics. If those guys tried and moved on then we mere mortals don't stand a chance. Again, not shitting on your work. I think any dev is great but you have to know where to really do work and expect a payoff.
So long, and thanks for all the fish
User avatar
Gustavo6046
Godlike
Posts: 1462
Joined: Mon Jun 01, 2015 7:08 pm
Personal rank: Resident Wallaby
Location: Porto Alegre, Brazil
Contact:

Re: Idea: Caio Engine, UScript physics in UE1

Post by Gustavo6046 »

PrinceOfFunky wrote:Why do junctions need meshes?
What, you like invisible stuff? :ironic:
Then don't need, but it would be... the only use.

PrinceOfFunky wrote:Why, if the expression {junction.previous == self} is true, then it means the junction is a "next" junction? Next to what?
Because if a junction A which is the previous of B then B is obviously the next of A. :)
JackGriffin wrote:I'd junk this entire idea. It's not because it's not a good idea, it's just that a ton of the physics of actors is baked into the native code. You'll never get ragdoll unless you hide the player and create this whole new effect class with models, etc. It will never be the player though.

Think of it like this: You can't have a true spider physics in Unreal/UT because of the primitive nature of the engine. In essence you are limited to shoot-bumping like the pylon or over-riding the velocity of the actor.

Ferali really wanted to do this too with vehicles and gave up. So did dots with his version of physics. If those guys tried and moved on then we mere mortals don't stand a chance. Again, not shitting on your work. I think any dev is great but you have to know where to really do work and expect a payoff.
You did really make my day with that explanation! Guess I'll instead work with scripted actions, reducing more my project count and making more time avaiable :)
"Everyone is an idea man. Everybody thinks they have a revolutionary new game concept that no one else has ever thought of. Having cool ideas will rarely get you anywhere in the games industry. You have to be able to implement your ideas or provide some useful skill. Never join a project whose idea man or leader has no obvious development skills. Never join a project that only has a web designer. You have your own ideas. Focus on them carefully and in small chunks and you will be able to develop cool projects."

Weapon of Destruction
Post Reply