PDA

View Full Version : Variables in arrays not unique across multiple instances.

Celarion
07-10-2006, 05:27 AM
Hey guys, I've created the following code in preparation for a flocking AI system for Era. There is a problem, however, that when I spawn multiple instances (or even create separate autonomous NPCs), their variables become entangled (they move in exactly the same manner at exactly the same time). Is there any way to ensure that the variables are unique for every instance of the class?

// Created by Frans N Henskens (*Celarion)
// Boid NPC
// Uses attraction, repulsion avoidance and
// guidance rules to simulate flocking
// and targetseeking/avoiding behaviour.
//
// Currently reduces a base desired movement vector to
// a desired movement angle, applies trigonometric
// functions to retrieve a unit vector,
// applies acceleration then adds this to current
// velocity vector.
// This is checked against and constrained by a
// maximum velocity vector.
//
// To Do:
// Implement boid rules of separation, alignment and cohesion
// Assign appropriate biases to each of the resultant vectors
// Replace target point with sum of weighted rule vectors to
// achieve desired movement angle.
// Possibly implement breaking, "perching"
//
// Long Term:
// Implement advanced rules: predators, pathways, prey/food.
//
// Algorithm Complexity (On^2)
//
// NOTE: In Graal, the y-axis is inverted (counting up in a
// downwards direction from the top-left of the screen)
// Thus, all calculations to do with the y-axis have to be
// negated.

function onCreated()
{

// DEPRECATED
targets = {23,17};

// Initialise variables
z = -1;
velocity = {0,0};
acceleration = 1;
maxVelocity = 3;
granularity = 0.05;
distanceThreshold = 3;
// owner = "Celarion";
showcharacter();
bodyimg = "body13.png";
setcharani("sit", " ");
// setimg("era_hachibluering.png");
setshape(1,0,0);
message(" ");
// Set timer to start behavioural thread
setTimer(granularity);
}

function getUnitVector(vec) {
angle = getangle(vec[0], vec[1]);
calcVec[0] = cos(angle);
calcVec[1] = -sin(angle);
return calcVec;
}
function onPlayerEnters() {
onTimeout();
}
function onTimeOut() {
// DEPRECATED
// Calculate desired movement vector
found = 0;
spl = findplayer(owner);
if (spl != "")
{
found = 1;
targets[0] = spl.x;
targets[1] = spl.y;
if (level.name != spl.level.name) {
this.warpto(spl.level.name, targets[0], targets[1]);
}
}
if (found == 0) {
velocity = {0,0};
setTimer(3);
return;
}
dist[0] = targets[0] - x;
dist[1] = targets[1] - y;

// Locate boids
// Calculate rule 1 :: Separation
// Calculate rule 2 :: Cohesion
// Calculate rule 3 :: Alignment
// Calculate resultant rule vector

// Calculate unit vector for resultant velocity
uv = getUnitVector(dist);
velocity[0] += acceleration * uv[0];
velocity[1] += acceleration * uv[1];

// Check resultant velocity vector amplitude against max velocity
if ((velocity[0]^2 + velocity[1]^2)^(1/2) > maxVelocity) {
uv = getUnitVector(velocity);
velocity[0] = maxVelocity * uv[0];
velocity[1] = maxVelocity * uv[1];
}

// Apply resultant movement vector
x = x + velocity[0];
y = y + velocity[1];

// Set timer to start next behavioural thread
setTimer(granularity);
}
function onPlayerChats()
{
if (player.chat.starts("/"))
{
toks = player.chat.tokenize();
if (toks[0] == "/destroy")
{
if (owner == toks[1])
{
destroy();
}
}
}
}

Thanks!
Frans

upsilon
07-10-2006, 07:28 PM
I think you should qualify the object-local variables with 'this.'. I dont really know the default scope of unqualified variable in the body of functions/events, but i think it either makes them class local, or even global variables.

this.acceleration = 1;
this.granularity = 0.05;
... etc

and i think the general convention for function local-variable, is to just make them members of the temp object, like:

function foo(x,y) {
temp.baz = x*y;
this.bar = temp.baz * 11;
}

to avoid unintentionally over writing global variables with the same name.

ApothiX
07-12-2006, 03:03 AM
@upsilon: The default scope for un-prefixed is global. They can be accessed by any script that is currently running.

Celarion
07-12-2006, 03:51 PM
I tried prefixing the variables with "this.". The entire thing stopped working. Perhaps I missed something.
*shrugs*

upsilon
07-12-2006, 06:51 PM
The default scope for un-prefixed is global. They can be accessed by any script that is currently running.
07-10-2006 11:28 AM

gotcha

I tried prefixing the variables with "this.". The entire thing stopped working. Perhaps I missed something.

Im guessing they just arent moving? or are they not even drawing? if you could, just paste what you have now, maybe something will stick out.

without seeing the code, all i can think of, is that maybe you qualified the variables x and y wrong when you were prefixing things. If it worked before without those prefixed, i would leave those two as they were originally.

jake13jake
07-13-2006, 09:39 PM
the scope of non-prefixed vars:
An NPC's static variables - local to the NPC (can be written x= or this.x=).
Any other vars that are built-in (ex. players, npcs arrays)
A function parameter- local to the function.
Otherwise, it's a global variable that all scripts share.

If you're using a variable temporarily, like send some data into a function and simply returning a value, I'd use a temp.var.

Celarion
08-21-2006, 03:58 AM
Okay, so I've scoped the vars properly with this. now, and that works fine for individually spawned NPC objects.

There is, however, still the same problem when instantiating instances of this code as a class - despite this.name attribute being unique for each instance. I believe that there may be an issue with the compiler/interpreter for the scripting language w/r/t classes: Perhaps only copying array header/reference when instantiating objects (thus leaving the reference to the start of the first array, rather than creating a new array for each object)?

Awesome if it could be fixed, anyway.

Cheers,
Cel

// Created by Frans N Henskens (*Celarion)
// Boid NPC
// Uses attraction, repulsion avoidance and
// guidance rules to simulate flocking
// and targetseeking/avoiding behaviour.
//
// Currently reduces a base desired movement vector to
// a desired movement angle, applies trigonometric
// functions to retrieve a unit vector,
// applies acceleration then adds this to current
// velocity vector.Celarion
// This is checked against and constrained by a
// maximum velocity vector.
//
// To Do:
// Implement boid rules of separation, alignment and cohesion
// Assign appropriate biases to each of the resultant vectors
// Replace target point with sum of weighted rule vectors to
// achieve desired movement angle.
// Possibly implement breaking, "perching"
//
// Long Term:
// Implement advanced rules: predators, pathways, prey/food.
//
// Algorithm Complexity (On^2)
//
// NOTE: In Graal, the y-axis is inverted (counting up in a
// downwards direction from the top-left of the screen)
// Thus, all calculations to do with the y-axis have to be
// negated.

function onCreated()
{

// DEPRECATED
targets = {23,17};

// Initialise variables
this.z = -1;
this.dist = {0,0};
this.uv = {0,0};
this.acceleration = 1;
this.maxVelocity = 3;
this.granularity = 0.05;
this.distanceThreshold = 3;
this.owner = "Celarion";
showcharacter();
this.bodyimg = "body13.png";
setcharani("sit", " ");
// setimg("era_hachibluering.png");
setshape(1,0,0);
message(" ");
// Set timer to start behavioural thread
setTimer(this.granularity);
}

function getUnitVector(vec) {
temp.angle = getangle(vec[0], vec[1]);
temp.calcVec = {cos(temp.angle), -sin(temp.angle)};
return temp.calcVec;
}
function onPlayerEnters() {
onTimeout();
}
function onTimeOut() {
// DEPRECATED
// Calculate desired movement vector
// findplayer("Celarion").chat = "Owner: " @ this.target.name @ ", " @ "Targets: x=" @ this.targets[0] SPC "y=" @ this.targets[1] @ ", Distances: x=" @ this.dist[0] @ ", y=" @ this.dist[1];
this.target = findplayer(this.owner);
if (this.target != "")
{
this.targets = {this.target.x, this.target.y};
if (this.level.name != target.level.name) {
warpto(target.level.name, this.targets[0], this.targets[1]);
}
} else {
this.velocity = {0,0};
setTimer(0);
return;
}
this.dist = {this.targets[0] - this.x, this.targets[1] - this.y};

// Locate boids
// Calculate rule 1 :: Separation
// Calculate rule 2 :: Cohesion
// Calculate rule 3 :: Alignment
// Calculate resultant rule vector

// Calculate unit vector for resultant velocity
this.uv = getUnitVector(this.dist);
this.velocity[0] += this.acceleration * this.uv[0];
this.velocity[1] += this.acceleration * this.uv[1];

// Check resultant velocity vector amplitude against max velocity
if ((this.velocity[0]^2 + this.velocity[1]^2)^(1/2) > this.maxVelocity) {
this.uv = getUnitVector(this.velocity);
this.velocity = {this.maxVelocity * this.uv[0], this.maxVelocity * this.uv[1]};
}

// Apply resultant movement vector
this.x = this.x + this.velocity[0];
this.y = this.y + this.velocity[1];

// Set timer to start next behavioural thread
setTimer(this.granularity);
}

function onPlayerChats()
{
if (player.chat.starts("/"))
{
temp.toks = player.chat.tokenize();
if (temp.toks[0] == "/destroy")
{
if (this.owner == temp.toks[1])
{
destroy();
}
}
}
}

EDIT:: Thanks Skyld.

ApothiX
08-21-2006, 07:04 AM
Use code tags to post code, not quote tags. That is extremely hard to read in the state that it's in.

xXziroXx
08-21-2006, 01:55 PM
Or even better, PHP tags.

Celarion
08-21-2006, 03:28 PM
Thanks for that tip.
Is my code faulty, or is the engine lacking that functionality. If the latter is so, do you know of any workarounds I could implement?