[Solved] Shifting an array of vectors

All topics about ZGameEditor goes here.

Moderator: Moderators

Post Reply
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

[Solved] Shifting an array of vectors

Post by Ats »

Hello. I've started working on waypoints, so the enemies could move around in Omeganaut.
So I made a little test, but I'm not sure about the method, nor how to shift an array in ZGE...
So it's not working as some waypoints are disappearing during the shift... What do I do wrong?

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZExpression>
      <Expression>
<![CDATA[points.SizeDim1 = 0;

ship.Translate.X = 0;
ship.Translate.Y = -4;]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[time += App.DeltaTime;

// Append to array

if (time > 0.6)
{
  time = 0;
  points.SizeDim1++;
  points[points.SizeDim1-1] = vector2(random(0,5), 5);
}

// Move everything

//for(int i = 0; i < points.SizeDim1; i++)
for (int i = points.SizeDim1-1; i >= 0; i--)
{
  points[i].Y -= 0.1;
  if (points[i].Y < -5)
  {
    // Shift array ???

    for (int j = points.SizeDim1-1; j >= 1; j--)
    {
      points[j-1] = points[j];
    }
    points.SizeDim1--;
    trace(intToStr(points.SizeDim1));
  }
}

// move ship

if (points.SizeDim1)
{
  int p = 0;
  while (ship.Translate.Y > points[p].Y) p ++;
  ship.Translate.X += (points[p].X - ship.Translate.X) * 0.04;
  ship.Translate.Y += (points[p].Y - ship.Translate.Y) * 0.001;
}]]>
      </Expression>
    </ZExpression>
  </OnUpdate>
  <OnRender>
    <Repeat Name="repeat">
      <OnIteration>
        <ZExpression>
          <Expression>
<![CDATA[int i = repeat.Iteration;
transform.translate.X = points[i].X;
transform.translate.Y = points[i].Y;]]>
          </Expression>
        </ZExpression>
        <RenderTransformGroup Name="transform" Translate="1.2062 2.5 0">
          <Children>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
      </OnIteration>
      <WhileExp>
<![CDATA[//this.Iteration=current iteration nr. Return false to end loop.

return this.Iteration < points.SizeDim1;]]>
      </WhileExp>
    </Repeat>
    <RenderTransformGroup Name="ship" Translate="1.0845 -0.0736 0">
      <Children>
        <RenderSetColor Color="1 0 0 1"/>
        <RenderSprite/>
      </Children>
    </RenderTransformGroup>
  </OnRender>
  <Content>
    <Array Name="points" Type="6" SizeDim1="31"/>
    <Variable Name="time"/>
  </Content>
</ZApplication>
Last edited by Ats on Wed Sep 07, 2022 9:20 am, edited 1 time in total.
User avatar
Kjell
Posts: 1915
Joined: Sat Feb 23, 2008 11:15 pm

Re: Shifting an array of vectors

Post by Kjell »

Hi Ats,
Ats wrote: Sun Sep 04, 2022 11:26 pmWhat do I do wrong?
By trying to shift the point vectors back-to-front, you end up copying the last point of your array into all indices. Your inner loop should go front-to-back instead.

By the way, i'd recommend trying to avoid shifting around elements in your arrays unless absolutely necessary .. in a lot of situations there are smarter & better-performing alternatives ( such as a linked-list or a "pool" ). But if you absolutely must, at least try to prevent shifting / moving elements multiple times per frame .. for example:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZExpression>
      <Expression>
<![CDATA[// Clear array

Points.SizeDim1 = 0;]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[// Update time & insert a new point each half a second

Time += App.DeltaTime;

while(Time > 0.5)
{
  Time -= 0.5;
  Points[Points.SizeDim1++] = vector2(random(0, 5), 5);
}

// Update the position of all points

for(int i=0; i<Points.SizeDim1; i++)
{
  Points[i].Y -= 6*App.DeltaTime;
}

// Count the number of points that need to be removed

// In this example this is a little silly since you'd
// need a terrible framerate for the count to ever go
// beyond 1, but anyway. And of course once the points
// shouldn't be removed in "spawn" order you can't use
// this tactic anymore.

int count = 0;

while(count < Points.SizeDim1)
{
  if(Points[count].Y < -5)
  {
    count++;
    continue;
  }

  break;
}

// Remove the points by shifting the array

if(count)
{
  for(int i=0; i<Points.SizeDim1-count; i++)
  {
    Points[i] = Points[i+count];
  }

  Points.SizeDim1 -= count;
}]]>
      </Expression>
    </ZExpression>
  </OnUpdate>
  <OnRender>
    <Repeat Name="PointsRepeat">
      <OnIteration>
        <ZExpression>
          <Expression>
<![CDATA[//

int i = PointsRepeat.Iteration;

//

PointTransform.Translate.X = Points[i].X;
PointTransform.Translate.Y = Points[i].Y;]]>
          </Expression>
        </ZExpression>
        <RenderTransformGroup Name="PointTransform">
          <Children>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
      </OnIteration>
      <WhileExp>
<![CDATA[//

return Iteration < Points.SizeDim1;]]>
      </WhileExp>
    </Repeat>
  </OnRender>
  <Content>
    <Variable Name="Time"/>
    <Array Name="Points" Type="6"/>
  </Content>
</ZApplication>
K
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Shifting an array of vectors

Post by Ats »

you end up copying the last point of your array into all indices
Hahaha now I see it clearly. I shouldn't code that kind of stuff while watching Game of Thrones :lol:

Thanks for your corrections. The shift only happens then the waypoint will be destroyed, so once every 1 or 2 seconds, but I'm going to I'm going to iterate on that example. Is that the linked-list method? https://leetcode.com/problems/linked-list-cycle/
User avatar
Kjell
Posts: 1915
Joined: Sat Feb 23, 2008 11:15 pm

Re: Shifting an array of vectors

Post by Kjell »

Hi Ats,
Ats wrote: Mon Sep 05, 2022 9:10 amThe shift only happens then the waypoint will be destroyed, so once every 1 or 2 seconds
That's something you generally want to avoid .. processor intensive stuff that only happens every once in a while and can't be spread over multiple frames. In video games you want to keep your processor load as flat as possible over time.
Ats wrote: Mon Sep 05, 2022 9:10 amIs that the linked-list method? https://leetcode.com/problems/linked-list-cycle/
That looks like a example / assignment to determine whether or not a so-called "cycle" exists in a linked-list. For more generic information on a linked-list ( and other data structures ) you can just check Wikipedia. Also, you can find a timing cheatsheet for various common data structures here.

Anyway, it really depends on your specific situation what data structure / approach will give you the best & most "flat" performance. For example, if you don't care about the order in which points are stored in your array, you can use the following ( super simple ) approach .. no shifting required.

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZExpression>
      <Expression>
<![CDATA[// Clear array

Points.SizeDim1 = 0;]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[// Update time & insert a new point each half a second

Time += App.DeltaTime;

while(Time > 0.5)
{
  Time -= 0.5;
  Points[Points.SizeDim1++] = vector2(random(0, 5), 5);
}

// Update the position of all points

for(int i=0; i<Points.SizeDim1; i++)
{
  Points[i].Y -= 6*App.DeltaTime;

  if(Points[i].Y < -5)
  {
    Points[i--] = Points[Points.SizeDim1-1];
    Points.SizeDim1--;
  }
}]]>
      </Expression>
    </ZExpression>
  </OnUpdate>
  <OnRender>
    <Repeat Name="PointsRepeat">
      <OnIteration>
        <ZExpression>
          <Expression>
<![CDATA[//

int i = PointsRepeat.Iteration;

//

PointTransform.Translate.X = Points[i].X;
PointTransform.Translate.Y = Points[i].Y;]]>
          </Expression>
        </ZExpression>
        <RenderTransformGroup Name="PointTransform">
          <Children>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
      </OnIteration>
      <WhileExp>
<![CDATA[//

return Iteration < Points.SizeDim1;]]>
      </WhileExp>
    </Repeat>
  </OnRender>
  <Content>
    <Variable Name="Time"/>
    <Array Name="Points" Type="6"/>
  </Content>
</ZApplication>
K
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Shifting an array of vectors

Post by Ats »

This is getting somewhere. But I have to admit that the test I run in order to know when a ship has reached a waypoint and has to choose the next one is seriously horrible... I'm not inspired these days :oops:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZExpression>
      <Expression>
<![CDATA[// Clear array

Points.SizeDim1 = 0;
Points.SizeDim2 = 3;]]>
      </Expression>
    </ZExpression>
    <ZLibrary>
      <Source>
<![CDATA[inline float max(float A, float B) { return A >= B ? A : B; }
inline float min(float A, float B) { return A <= B ? A : B; }]]>
      </Source>
    </ZLibrary>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[// Update time & insert a new point each second

Time += App.DeltaTime;


while(Time > 1)
{
  Time -= 1;
  Points.SizeDim1++;
  Points[Points.SizeDim1-1, 0] = vector2(random(-3, 2), 5);
  Points[Points.SizeDim1-1, 1] = vector2(random(0, 2), 5);
  Points[Points.SizeDim1-1, 2] = vector2(random(3, 2), 5);
}

// Update the position of all points

for(int i=0; i<Points.SizeDim1; i++)
{
  Points[i, 0].Y -= 6*App.DeltaTime;
  Points[i, 1].Y = Points[i, 0].Y;
  Points[i, 2].Y = Points[i, 0].Y;

  if(Points[i, 0].Y < -5)
  {
    Points[i, 0] = Points[Points.SizeDim1-1, 0];
    Points[i, 1] = Points[Points.SizeDim1-1, 1];
    Points[i, 2] = Points[Points.SizeDim1-1, 2];
    i--;
    Points.SizeDim1--;
  }
}]]>
      </Expression>
    </ZExpression>
    <Timer Interval="2">
      <OnTimer>
        <ZExpression>
          <Expression>
<![CDATA[// Add ship
@SpawnModel(Model: ship);]]>
          </Expression>
        </ZExpression>
      </OnTimer>
    </Timer>
  </OnUpdate>
  <OnRender>
    <Repeat Name="PointsRepeat">
      <OnIteration>
        <ZExpression>
          <Expression>
<![CDATA[//

int i = PointsRepeat.Iteration;

//

PointTransform1.Translate.X = Points[i, 0].X;
PointTransform1.Translate.Y = Points[i, 0].Y;

PointTransform2.Translate.X = Points[i, 1].X;
PointTransform2.Translate.Y = Points[i, 1].Y;

PointTransform3.Translate.X = Points[i, 2].X;
PointTransform3.Translate.Y = Points[i, 2].Y;]]>
          </Expression>
        </ZExpression>
        <RenderTransformGroup Name="PointTransform1" Scale="0.1 0.1 0.1" Translate="-4.985 4.3285 0">
          <Children>
            <RenderSetColor Color="0.502 1 0.502 1"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
        <RenderTransformGroup Name="PointTransform2" Scale="0.1 0.1 0.1" Translate="1.2497 4.3285 0">
          <Children>
            <RenderSetColor Color="1 1 0.502 1"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
        <RenderTransformGroup Name="PointTransform3" Scale="0.1 0.1 0.1" Translate="1.5703 4.3285 0">
          <Children>
            <RenderSetColor Color="1 0.502 0.502 1"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
      </OnIteration>
      <WhileExp>
<![CDATA[//

return Iteration < Points.SizeDim1;]]>
      </WhileExp>
    </Repeat>
  </OnRender>
  <Content>
    <Variable Name="Time"/>
    <Array Name="Points" Type="6" Dimensions="1" SizeDim1="2" SizeDim2="3"/>
    <Model Name="ship" Position="0 -5 0">
      <Definitions>
        <Variable Name="shipPath" Type="4"/>
        <Variable Name="shipPathNumber" Type="1"/>
      </Definitions>
      <OnSpawn>
        <ZExpression>
          <Expression>
<![CDATA[shipSetColor.Color.R = rnd();
shipSetColor.Color.G = rnd();
shipSetColor.Color.B = rnd();

ship.Position.X = random(0, 5);
ship.Position.Y = -5;

shipPathNumber = 0;]]>
          </Expression>
        </ZExpression>
      </OnSpawn>
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[// Move ship

ship.Position.Y += 0.01;
if (ship.Position.Y > 5) @RemoveModel();

if (points.SizeDim1)
{
  int p = 0;
  while (ship.Position.Y > points[p, 0].Y)
  {
    // Choose next waypoint, but the test isn't great at all...
    float dif = (ship.Position.Y - points[p, 0].Y) * 10;
    if (dif < 1) shipPath = round(rnd() * 2);

    // To avoid getting out of the points array
    if (p < points.SizeDim1 - 1) p ++;
    else break;

  }

  float move = min(max((points[p, shipPath].X - ship.Position.X) * 0.04, -0.2), 0.2);
  ship.Position.X += move;
  shipTransform.Rotate.Z = -move;
}]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <RenderTransformGroup Name="ShipTransform" Scale="0.2 0.4 0">
          <Children>
            <RenderSetColor Name="shipSetColor" Color="0.502 0.502 1 1"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
      </OnRender>
    </Model>
  </Content>
</ZApplication>
Oh, I just saw your last example. I'm going to take a look :wink:

Edit:
That's definitely simpler and better. Real smart!
I updated my example with it.
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Shifting an array of vectors

Post by Ats »

All right! Choosing the next waypoint only once was easier to do with the camera moving on the Y axis, instead of moving each and every waypoint towards down:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FixedFrameRate="60" CameraPosition="0 33741.9414 10" FileVersion="2">
  <OnLoaded>
    <ZExpression>
      <Expression>
<![CDATA[// Clear array

Points.SizeDim1 = 0;
Points.SizeDim2 = 3;

@SpawnModel(Model: ship);]]>
      </Expression>
    </ZExpression>
    <ZLibrary>
      <Source>
<![CDATA[inline float max(float A, float B) { return A >= B ? A : B; }
inline float min(float A, float B) { return A <= B ? A : B; }]]>
      </Source>
    </ZLibrary>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[// Update time & insert a new point each second

Time += App.DeltaTime;


while(Time > NextTime)
{
  Time -= NextTime;
  NextTime = 0.5 + rnd() * 0.5;

  Points.SizeDim1++;
  Points[Points.SizeDim1-1, 0] = vector2(random(-3, 2), App.CameraPosition.Y + random(6,1));
  Points[Points.SizeDim1-1, 1] = vector2(random(0, 2), App.CameraPosition.Y + random(6,1));
  Points[Points.SizeDim1-1, 2] = vector2(random(3, 2), App.CameraPosition.Y + random(6,1));
}

// Update the position of all points

App.CameraPosition.Y += 6*App.DeltaTime;

for(int i=0; i<Points.SizeDim1; i++)
{
  if(Points[i, 0].Y < App.CameraPosition.Y-5)
  {
    Points[i, 0] = Points[Points.SizeDim1-1, 0];
    Points[i, 1] = Points[Points.SizeDim1-1, 1];
    Points[i, 2] = Points[Points.SizeDim1-1, 2];
    i--;
    Points.SizeDim1--;
  }
}]]>
      </Expression>
    </ZExpression>
    <Timer Interval="4">
      <OnTimer>
        <ZExpression>
          <Expression>
<![CDATA[// Add ship
@SpawnModel(Model: ship);]]>
          </Expression>
        </ZExpression>
      </OnTimer>
    </Timer>
  </OnUpdate>
  <OnRender>
    <Repeat Name="PointsRepeat">
      <OnIteration>
        <ZExpression>
          <Expression>
<![CDATA[//

int i = PointsRepeat.Iteration;

//

PointTransform1.Translate = Points[i, 0];
PointTransform2.Translate = Points[i, 1];
PointTransform3.Translate = Points[i, 2];]]>
          </Expression>
        </ZExpression>
        <RenderTransformGroup Name="PointTransform1" Scale="0.1 0.1 0.1" Translate="-3.0435 33742.9766 0">
          <Children>
            <RenderSetColor Color="0.502 1 0.502 1"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
        <RenderTransformGroup Name="PointTransform2" Scale="0.1 0.1 0.1" Translate="0.319 33743.7656 0">
          <Children>
            <RenderSetColor Color="1 1 0.502 1"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
        <RenderTransformGroup Name="PointTransform3" Scale="0.1 0.1 0.1" Translate="2.0288 33742.3867 0">
          <Children>
            <RenderSetColor Color="1 0.502 0.502 1"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
      </OnIteration>
      <WhileExp>
<![CDATA[//

return Iteration < Points.SizeDim1;]]>
      </WhileExp>
    </Repeat>
  </OnRender>
  <Content>
    <Array Name="Points" Type="6" Dimensions="1" SizeDim1="2" SizeDim2="3"/>
    <Variable Name="Time"/>
    <Variable Name="nextTime"/>
    <Model Name="ship" Position="0 -5 0">
      <Definitions>
        <Variable Name="shipPath" Type="4"/>
        <Variable Name="shipNextPoint" Type="6"/>
      </Definitions>
      <OnSpawn>
        <ZExpression>
          <Expression>
<![CDATA[shipSetColor.Color.R = rnd();
shipSetColor.Color.G = rnd();
shipSetColor.Color.B = rnd();

ship.Position.X = random(0, 5);
ship.Position.Y = App.CameraPosition.Y - 5;

if (points.SizeDim1)
{
  shipPath = round(rnd() * 2);
  shipNextPoint = Points[0, shipPath];
}]]>
          </Expression>
        </ZExpression>
      </OnSpawn>
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[// Move ship

int nextPoint = 0;

if (points.SizeDim1)
{
  // Ship has passed his next waypoint
  if (ship.Position.Y > shipNextPoint.Y)
  {
    // Find position in points array
    while (ship.Position.Y > points[nextPoint, shipPath].Y)
    {
      // To avoid getting out of the points array
      if (nextPoint < points.SizeDim1 - 1) nextPoint++;
      else break;
    }

    if (points[nextPoint, shipPath].Y > ship.Position.Y)
    {
      shipPath = round(rnd() * 2);
      shipNextPoint = Points[nextPoint, shipPath];
    }
    else
    {
      shipNextPoint.Y = ship.Position.Y + 2;
    }
  }

  ship.Position.Y += 6.5 * App.DeltaTime;
  if (ship.Position.Y > App.CameraPosition.Y + 5) @RemoveModel();

  float move = min(max((shipNextPoint.X - ship.Position.X) * 0.1, -0.2), 0.2);
  ship.Position.X += move;
  shipTransform.Rotate.Z = -move;
}

fartSetColor.Color = shipSetColor.Color;
@SpawnModel(Model:Fart, UseSpawnerPosition:1);]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <RenderTransformGroup Name="ShipTransform" Scale="0.2 0.4 0">
          <Children>
            <RenderSetColor Name="shipSetColor" Color="0.502 0.502 1 1"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
      </OnRender>
    </Model>
    <Model Name="fart">
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[fartTransform.Scale -= 0.2 * App.DeltaTime;
if (fart.Position.Y < App.CameraPosition - 5 || fartTransform.Scale <= 0) @RemoveModel();]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <RenderTransformGroup Name="fartTransform" Scale="0.16 0.16 0">
          <Children>
            <RenderSetColor Name="fartSetColor" Color="0.9326 0.8798 0.6672 1"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
      </OnRender>
    </Model>
  </Content>
</ZApplication>
Now let's add that into the game!
Post Reply