sektor2111 wrote:A question:
It's not good for particle to be a projectile without damage and using ProcessTouch ?
I guess there is doable some "back to home" thing when ProcessTouch is getting called + see what does it happen if they fall in void... we might have "cute" maps with such options...
Well I converted Particle into a Projectile, sadly ProcessTouch() only gives the HitLocation, not the HitNormal, well at least there's something more now, so ty.
The problem is that the calculations inside Tick() seem to be calculated more than once before a screen update.
This is how I changed the class (The friction change which has nothing to do with this topic):
Code: Select all
/*
* Supposed to be done by a community
*/
class Particle extends Projectile;
var ParticleSystem particleSys;
var vector friction;
var vector fixedForce;
var vector bounceDir;
var Actor lastHitActor; //Used for bounces.
var vector ParticleHitNormal;
var Actor ParticleHitWall;
var int bounceCounter;
var bool bHitEffect;
auto state Init {
event BeginState() {
bCollideWorld = true;
particleSys = ParticleSystem(owner);
if (particleSys == None)
destroy();
if (particleSys.localPlayer == None)
particleSys.FindLocalPlayer();
if (particleSys.localPlayer == None)
destroy();
GotoState('Resetting');
}
}
event FellOutOfWorld() {
GotoState('Resetting');
}
event Timer() {
GotoState('Resetting');
}
state Resetting
{
event BeginState() {
bHidden = true;
Setup();
}
function Setup() {
local vector relativeLoc;
local int i;
for (i = 0; i < particleSys.MaxSetLocationAttempts; ++i) {
relativeLoc = particleSys.Location + particleSys.RandRangeVec(particleSys.minSpawnLoc, particleSys.maxSpawnLoc);
if (SetLocation(relativeLoc))
if (particleSys.allowNewLoc(self))
if (!particleSys.bEmitInZone || (particleSys.bEmitInZone && (Region.Zone == particleSys.Region.Zone)))
if ((particleSys.maxDistFromPlayer == 0) || (VSize(Location - particleSys.localPlayer.Location) <= particleSys.maxDistFromPlayer))
if (!particleSys.bSpawnInViewport || (particleSys.bSpawnInViewport && (PlayerCanSeeMe())))
break;
}
bHidden = i == particleSys.MaxSetLocationAttempts;
if (bHidden) {
GotoState('Resetting');
} else {
//Actual reset.
fixedForce = particleSys.RandRangeVec(particleSys.minFixedForce, particleSys.maxFixedForce);
SetCollision(particleSys.bBlockByActors, false, false);
bounceCounter = 0;
Velocity *= 0;
Mass = particleSys.particlesMass;
Texture = particleSys.particlesTexture;
setTimer(particleSys.forcedResetInterval, false);
GotoState('StartPhysics');
}
}
}
function ApplyForce(vector force) {
Acceleration += (force / Mass);
}
//Actually not rly all, the bounce forces are applied later.
function ApplyAllForces() {
local ForceInfo forceInfo;
local float distFromForce;
local vector actualForce;
ApplyForce(friction);
ApplyForce(Region.Zone.ZoneGravity * Mass);
ApplyForce(Region.Zone.ZoneVelocity);
ApplyForce(fixedForce);
foreach AllActors(class'ForceInfo', forceInfo) {
actualForce = forceInfo.force;
if (VSize(forceInfo.actionDist) != 0) {
distFromForce = VSize(Location - forceInfo.Location);
if (distFromForce < 0)
distFromForce *= -1;
if (distFromForce > (FMax(FMax(forceInfo.actionDist.X, forceInfo.actionDist.Y), forceInfo.actionDist.Z)))
continue;
actualForce.X = map(distFromForce, 0, forceInfo.actionDist.X, forceInfo.force.X, 0);
actualForce.Y = map(distFromForce, 0, forceInfo.actionDist.Y, forceInfo.force.Y, 0);
actualForce.Z = map(distFromForce, 0, forceInfo.actionDist.Z, forceInfo.force.Z, 0);
}
ApplyForce(actualForce - forceInfo.Location);
}
}
static function limitVelocity(Actor runner, vector limit) {
//Limit positive velocity.
if (runner.Velocity.X > limit.X)
runner.Velocity.X = limit.X;
if (runner.Velocity.Y > limit.Y)
runner.Velocity.Y = limit.Y;
if (runner.Velocity.Z > limit.Z)
runner.Velocity.Z = limit.Z;
//Limit negative velocity.
if (runner.Velocity.X < (limit.X * -1))
runner.Velocity.X = limit.X * -1;
if (runner.Velocity.Y < (limit.Y * -1))
runner.Velocity.Y = limit.Y * -1;
if (runner.Velocity.Z < (limit.Z * -1))
runner.Velocity.Z = limit.Z * -1;
}
static function vector calcHitNormalApprox(Actor act) {
local float max;
local vector positiveVel, hitNormal;
if (act.Velocity.X < 0)
positiveVel.X = (act.Velocity.X * -1);
if (act.Velocity.Y < 0)
positiveVel.Y = (act.Velocity.Y * -1);
if (act.Velocity.Z < 0)
positiveVel.Z = (act.Velocity.Z * -1);
max = FMax(FMax(positiveVel.X, positiveVel.Y), positiveVel.Z);
if (positiveVel.X == max)
hitNormal.X = 1;
else if (positiveVel.Y == max)
hitNormal.Y = 1;
else if (positiveVel.Z == max)
hitNormal.Z = 1;
return hitNormal;
}
function float map(float x, float inMin, float inMax, float outMin, float outMax) {
return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
}
function Hit(actor HitStuff, vector HitNormal) {
local HitEffect hitFX;
if ((HitStuff != None) && (VSize(HitNormal) == 0))
HitNormal = calcHitNormalApprox(self);
if ((particleSys.maxBounces <= 0) || ((particleSys.maxBounces > 0) && (bounceCounter >= particleSys.maxBounces))) {
ParticleHitNormal = HitNormal;
ParticleHitWall = HitStuff;
if (bHitEffect) {
hitFX = spawn(particleSys.hitEffectClass,,,Location);
if (hitFX != None)
hitFX.init(ParticleHitNormal, ParticleHitWall);
}
if (!particleSys.bResetOnLastHit)
GotoState('Idle');
else
GotoState('Resetting');
} else {
//FIXME - Only check for the last hti actor, shouldn't it be applied for any actor who's currently touching(maybe implementing it inside ApplyAllForces)?
bounceDir = HitNormal;
lastHitActor = HitStuff;
bounceCounter++;
}
}
event HitWall(vector HitNormal, actor HitWall) {
Hit(HitWall, HitNormal);
}
static function vector normalNoRound(vector vec) {
local float normCohefficent;
local vector normVec;
normCohefficent = (vec.x * vec.x) + (vec.y * vec.y) + (vec.z * vec.z);
normVec = vec / normCohefficent;
}
state Idle {
Ignores HitWall;
}
static function float getMagnitude(vector vec) {
return sqrt(vec.X^2 + vec.Y^2 + vec.Z^2);
}
state StartPhysics {
event BeginState() {
SetPhysics(PHYS_Projectile);
}
event EndState() {
//The particle will move back to the initial position, it needs to be non-collidable.
SetCollision(false);
SetPhysics(PHYS_None);
}
function ProcessTouch(Actor Other, Vector HitLocation) {
Hit(Other, vect(0,0,0));
}
event ZoneChange(ZoneInfo NewZone) {
particleSys.particleZoneChange(self, NewZone);
}
event Tick(float DeltaTime) {
local vector otherForce;
friction = (Velocity * -1);
friction = normalNoRound(friction);
if (!Region.Zone.bWaterZone)
friction *= Region.Zone.ZoneGroundFriction;
else
friction *= Region.Zone.ZoneFluidFriction;
ApplyAllForces();
//Calculate bounce velocity.
if (VSize(bounceDir) != 0) {
if (bounceDir.X != 0)
Velocity.X *= -1;
else if (bounceDir.Y != 0)
Velocity.Y *= -1;
else if (bounceDir.Z != 0)
Velocity.Z *= -1;
bounceDir *= 0;
}
if ((lastHitActor != None) && !lastHitActor.IsA('LevelInfo')) {
otherForce.X = lastHitActor.Velocity.X + DeltaTime;
otherForce.Y = lastHitActor.Velocity.Y + DeltaTime;
otherForce.Z = lastHitActor.Velocity.Z + DeltaTime;
ApplyForce(otherForce);
particleSys.localPlayer.PlayerReplicationInfo.PlayerName = (Level.TimeSeconds@Acceleration);
}
Log(Level.TimeSeconds@Acceleration);
lastHitActor = None;
Velocity += Acceleration * DeltaTime;
//Limiting the Velocity to the Zone Terminal Velocity.
limitVelocity(self, vect(1,1,1) * Region.Zone.ZoneTerminalVelocity);
// Reset acceleration
Acceleration *= 0;
}
}
event Destroyed() {
if (particleSys != None) {
particleSys.particlesCounter--;
particleSys.enable('timer');
}
}
defaultproperties
{
RemoteRole=ROLE_None
DrawType=DT_Sprite
Style=STY_Translucent
DrawScale=0.2
Mass=1
bBounce=True
CollisionRadius=1
CollisionHeight=1
bNetTemporary=False
bReplicateInstigator=False
LifeSpan=0
bDirectional=False
}
Note how in Tick() there's this piece of code:
Code: Select all
if ((lastHitActor != None) && !lastHitActor.IsA('LevelInfo')) {
otherForce.X = lastHitActor.Velocity.X + DeltaTime;
otherForce.Y = lastHitActor.Velocity.Y + DeltaTime;
otherForce.Z = lastHitActor.Velocity.Z + DeltaTime;
ApplyForce(otherForce);
particleSys.localPlayer.PlayerReplicationInfo.PlayerName = (Level.TimeSeconds@Acceleration);
}
Log(Level.TimeSeconds@Acceleration);
lastHitActor = None;
Velocity += Acceleration * DeltaTime;
There's a log right after ApplyForce(otherForce). That log shows the acceleration right after the velocity of the touching player has been applied to the particle, and in which Level.TimeSeconds the calculation was done.
The second log is right after, does the same but the previous "if" body could haven't been called, since it is called only when lastHitActor != None.
Right after the logs lastHitActor is set to None and the Acceleration is applied to the velocity.
I tried touching a falling on its Z axis Particle, while moving forward with the player, and this is the PlayerName log:
Code: Select all
2.981958 132.402542,15.717022,-50.481331
While I get 3 logs of the second log in, apparently, the same Level.TimeSeconds:
Code: Select all
ScriptLog: 2.981958 0.031926,-0.098551,-1000.660767
ScriptLog: 2.981958 132.402542,15.717022,-50.481331
ScriptLog: 2.981958 0.000000,0.000000,-1021.774353
I don't get it, is it like Velocity gets resetted? Cause of course the first time it bounces lastHitActor will exist but it will be set to None in Tick(), so if Tick() is called more than once before the screen update, it won't find the lastHitActor the other times.
Anyway, I changed Tick() like this:
Code: Select all
event Tick(float DeltaTime) {
local vector otherForce;
friction = (Velocity * -1);
friction = normalNoRound(friction);
if (!Region.Zone.bWaterZone)
friction *= Region.Zone.ZoneGroundFriction;
else
friction *= Region.Zone.ZoneFluidFriction;
ApplyAllForces();
//Calculate bounce velocity.
if (VSize(bounceDir) != 0) {
if (bounceDir.X != 0)
Velocity.X *= -1;
else if (bounceDir.Y != 0)
Velocity.Y *= -1;
else if (bounceDir.Z != 0)
Velocity.Z *= -1;
bounceDir *= 0;
}
Velocity += Acceleration * DeltaTime;
// Reset acceleration
Acceleration *= 0;
if ((lastHitActor != None) && !lastHitActor.IsA('LevelInfo')) {
otherForce.X = lastHitActor.Velocity.X + DeltaTime;
otherForce.Y = lastHitActor.Velocity.Y + DeltaTime;
otherForce.Z = lastHitActor.Velocity.Z + DeltaTime;
ApplyForce(otherForce);
Velocity += Acceleration;
// Reset acceleration
Acceleration *= 0;
}
lastHitActor = None;
//Limiting the Velocity to the Zone Terminal Velocity.
limitVelocity(self, vect(1,1,1) * Region.Zone.ZoneTerminalVelocity);
}
}
The bounce now works correctly. Wanna know why? Well, me too.
EDIT: I just understood DeltaTime /o\
Ok I "fixed" the Tick() with a better(I hope) implementation of DeltaTime, but now the bounces on player have the same issue of before lol.
This is the modified Tick():
Code: Select all
event Tick(float DeltaTime) {
friction = (Velocity * -1);
friction = normalNoRound(friction);
if (!Region.Zone.bWaterZone)
friction *= Region.Zone.ZoneGroundFriction;
else
friction *= Region.Zone.ZoneFluidFriction;
friction *= DeltaTime;
ApplyAllForces();
//Calculate bounce velocity.
if (VSize(bounceDir) != 0) {
if (bounceDir.X != 0)
Velocity.X *= -1;
else if (bounceDir.Y != 0)
Velocity.Y *= -1;
else if (bounceDir.Z != 0)
Velocity.Z *= -1;
bounceDir *= 0;
}
if ((lastHitActor != None) && !lastHitActor.IsA('LevelInfo'))
ApplyForce(lastHitActor.Velocity);
lastHitActor = None;
Velocity += Acceleration * DeltaTime;
//Limiting the Velocity to the Zone Terminal Velocity.
limitVelocity(self, vect(1,1,1) * Region.Zone.ZoneTerminalVelocity);
//Reset acceleration
Acceleration *= 0;
}