This basically creates a circle and saves each frame on a Dynamic Sprite array (size of 20).
I've noticed that this takes 2-4 seconds to execute, and the whole issue stands (i've commented parts of the code etc) inside the two while loops, is there a way I can make this faster/better?
//CODE IS EXECUTED TILL 20 Dynamic Sprite FRAMES ARE CREATED.
waveFrames[waveFrameC]=DynamicSprite.CreateFromExistingSprite(waveFrames[0].Graphic, true);//waveFrameC
DrawingSurface*area = waveFrames[waveFrameC].GetDrawingSurface();
DrawingSurface*refarea = waveFrames[0].GetDrawingSurface();//waveFrameC-1
float Size=6.0;//2.0;
float IncX=2.6;
int CircleSize=((wvc+1)-waveFrameC);
int incBy=waveFrameC*FloatToInt(Size, eRoundNearest);//*6;
int incByx=FloatToInt(IntToFloat(incBy)*IncX, eRoundNearest);
int CentralX=cEgo.x;
int CentralY=cEgo.y-150;
int x=CentralX+(incByx);
int x2=CentralX-(incByx);
int y=CentralY+(incBy);
int y2=CentralY-(incBy);//180-(incBy);
int d=x2;
int d1=0;
int f=y2;
int f1=0;
float difX;
float difY;
float sum;
float distance;
int checkDist;
float gx=IntToFloat(waveFrameC)*Size*IncX;
int getXv=FloatToInt(gx, eRoundNearest);
int getX=CentralX-getXv;
int getY=CentralY-(waveFrameC*6);
while (d < x && CircleSize>0)//while
{
f=y2;
f1=0;
while (f < y)
{
//CALCULATE DISTANCE BETWEEN TWO POINTS
difX = IntToFloat(CentralX) - IntToFloat(d);
difY = IntToFloat(CentralY) - IntToFloat(f);
difX = Maths.RaiseToPower(difX, 2.0);
difY = Maths.RaiseToPower(difY, 2.0);
sum = difX+difY;
distance = Maths.Sqrt(sum);
checkDist = FloatToInt(distance, eRoundNearest);
if (checkDist<= incBy && checkDist>incBy-CircleSize)
{
area.DrawingColor=refarea.GetPixel(getX+d1, getY + (f1-1));
area.DrawPixel(d, f);
}
f++;
f1++;
}
d1++;
d++;
}
area.Release();
refarea.Release();
waveFrameC++;
}
A common trick is to skip the square root and instead compare the squared distance to the square of the threshold(s).
Also, you don't need to do the difX/Y calculations as floats, do you?
Another approach might be to just use DrawingSurface.DrawCircle() and DynamicSprite.CopyTransparencyMask() to handle the masking, instead of drawing it manually. I'm not sure whether it's quicker, but it might be worth a try.
The issue doesn't stand on the calculations, albeit those can be improved, the issue lies on the two nested while loops
while (d < x && CircleSize>0)
{
f=y2;
f1=0;
while (f < y)
{
f++;
f1++;
}
d1++;
d++;
}
Even like this, no drawing whatsoever, the frame rate drops to 10 for a solid 2 seconds.
Well, have you calculated or checked how many loops that works out to be? Trying to follow your code, it appears to depend on the player x/y position.
It takes about 3 seconds to finish executing the code, around 120-150 loops. I did your adjustments, but same problem, I'm afraid.
Quote from: Dualnames on Sun 10/02/2019 15:06:51
Even like this, no drawing whatsoever, the frame rate drops to 10 for a solid 2 seconds.
Well, you're a very good coder (better than I am, for sure), so you don't need me to tell you that the AGS framerate isn't gonna drop to 10 fps just because you're counting to 150 each game loop.
Which means that something is off in your analysis. Either you're counting wrong and it's way more loops than that (150 inside
each loop, maybe?), or the main source of the slowdown is elsewhere.
I've noticed i was drawing a smaller circle than a rectangle, so now frames are at 29. 11 fps droprate.
Unless it's changed recently, pixel operations are too slow in AGS to be used outside of AGI resolutions.
Quote from: Jack on Sun 10/02/2019 21:23:10
Unless it's changed recently, pixel operations are too slow in AGS to be used outside of AGI resolutions.
I'd say it's definitely not something you'd like to do every game tick.
Quote from: Dualnames on Sun 10/02/2019 15:06:51
The issue doesn't stand on the calculations, albeit those can be improved, the issue lies on the two nested while loops
<...>
Even like this, no drawing whatsoever, the frame rate drops to 10 for a solid 2 seconds.
In the end was this confirmed or something else is causing slowdowns?
Could you post the newer code after changes made by Snarky's advice?
Also, why are you writing your own circle drawing instead of using DrawCircle? It seems like you need to combine certain images?. TBH I am too tired right now to understand what the code is doing without comments...
If I understand the code correctly, it draws a sprite that's a copy of some given area, except that in a ring between an outer circle and an inner circle, the coordinates are offset somehow (by a fixed amount, it seems). The size of the circles depends on a formula that uses the position of the player as well as the index of the sprite in an array, but working out the details without seeing it in action is beyond me. The excerpt we're seeing draws a single frame, it presumably exists within a bigger loop.
My interpretation is that it's meant to draw some sort of animated bubbles (or similar).
This is what it's supposed to be doing https://www.youtube.com/watch?v=HUxcmeT3LKs
I altered the code to make it run to 40 fps or so and made the effect a bit more subtle.
https://www.youtube.com/watch?v=vXQCf3zYez4&feature=youtu.be
Ignore the 2 characters, there are still WIP parts of this.
But you can see the frame drop is non existent.
int i=0;
while (i <20)
{
while (d < x && CircleSize>0)
{
f=y2;
f1=0;
while (f < y)
{
f++;
f1++;
}
d1++;
d++;
}
i++;
}
My main thing, is that the issues within AGS's inability to perform complex drawing stuff in real-time, isn't on the drawing functions. A big while like the one above, seems to be the problem in most cases. Like for example doing
int x=0;
while (x < width)//Width being 640 or 320 or whatever
{
int y=0;
while (y < height)//same as above
{
y++;
}
x++;
}
This loop without anything inside the while conditions, just as is, will cause framerate drop. I'm not sure if that's ameniable/fixable in any way, but I think it would solve a lot of issues. I wanted to use LUA for AGS but it's very risky given the state of that plugin. Regardless, I appreciate your work and I love you super hardcore <3.
Very cool.
Jack suggested lowering the resolution on IRC. Another way to control the number of iterations would be to draw the radius of the circle at various angles like a bunch of tiny sectors instead of checking all possible x/y values for the ones that lie in the circle.
Just wanted to add that this is a very cool effect!
One way I can see it be emulated is creating a circle with the desired alpha for transitions and stuff in a bigger size, then resizing it, creating a new sprite in a square area from the background, copying the prepared circle alpha on this square area you just got from the background, and do another resize at a slightly bigger size. It's a bit tricky but would avoid the while and could possibly run with no frame drops - need to test though.
It looks like you are modifying a ring about 150-200 pixels in diameter, so writing maybe 14000 pixels.
I seem to remember AGS was capable of updating 160x100=16000 pixels at about 20 FPS back in the day, so it should be able to manage 40 FPS now.
To ensure you are looping through the minimum number of iterations, I would work per scan line, and calculate the left and right extent of the circle, then decide if you are in a section with an inner unmodified part, and skip over the unmodified pixels, something like this:
// for a ring with small radius r and large radius R
for (y = (centerY - R) to (centerY + R))
h = abs(y - centerY)
halfOuterWidth = sqrt(R*R - h * h)
if (h < r)
halfInnerWidth = sqrt(r * r - h * h)
// modify pixels from centerX-halfOuterWidth to centerX-halfInnerWidth and from centerX+halfInnerWidth to centerX+halfOuterWidth
else
// modify pixels from centerX-halfOuterWidth to centerX+halfOuterWidth
Quote from: Dualnames on Sun 10/02/2019 22:50:45
I altered the code to make it run to 40 fps or so
I'd be interested to hear what you did to make that happen.
Quote from: Kweepa on Sat 16/02/2019 00:51:01
// for a ring with small radius r and large radius R
for (y = (centerY - R) to (centerY + R))
You don't need to run through the whole way from the top to bottom of the circle, because of symmetry; only the top half, then do the same operations except with the signs changed.
Quote from: Snarky on Sat 16/02/2019 06:52:01
You don't need to run through the whole way from the top to bottom of the circle, because of symmetry; only the top half, then do the same operations except with the signs changed.
Yeah, but the slow bit is the bit inside the inner loop, so it just complicates things.
This runs at 40 fps on my laptop.
I just move the ring in and out with a sin() for simplicity.
http://www.kweepa.org/step/ags/tech/Screech.zip (http://www.kweepa.org/step/ags/tech/Screech.zip)
// room script file
DynamicSprite *unchangedBg;
float time = 0.0;
// when I made the ring 20 pixels thick it slowed down
float ringThickness = 15.0;
function room_RepExec()
{
time += 0.1;
DynamicSprite *newBg = DynamicSprite.CreateFromExistingSprite(unchangedBg.Graphic);
float bigR = 60.0 + 39.0 * Maths.Sin(time);
float weeR = bigR - ringThickness;
float x;
float y;
DrawingSurface *oldSurf = unchangedBg.GetDrawingSurface();
DrawingSurface *newSurf = newBg.GetDrawingSurface();
for (y = -bigR; y < bigR; y += 1.0)
{
float p = bigR*bigR - y*y;
if (p > 1.0)
{
float how = Maths.Sqrt(p);
float q = weeR*weeR - y*y;
if (q > 1.0)
{
float hiw = Maths.Sqrt(q);
for (x = -how; x < -hiw; x+= 1.0)
{
float d = 1.0 - 3.0 / Maths.Sqrt(x*x + y*y);
newSurf.DrawingColor = oldSurf.GetPixel(100 + FloatToInt(x*d), 100 + FloatToInt(y*d));
newSurf.DrawPixel(100 + FloatToInt(x), 100 + FloatToInt(y));
}
for (x = hiw; x < how; x+= 1.0)
{
float d = 1.0 - 3.0 / Maths.Sqrt(x*x + y*y);
newSurf.DrawingColor = oldSurf.GetPixel(100 + FloatToInt(x*d), 100 + FloatToInt(y*d));
newSurf.DrawPixel(100 + FloatToInt(x), 100 + FloatToInt(y));
}
}
else
{
for (x = -how; x < how; x+= 1.0)
{
float d = 1.0 - 3.0 / Maths.Sqrt(x*x + y*y);
newSurf.DrawingColor = oldSurf.GetPixel(100 + FloatToInt(x*d), 100 + FloatToInt(y*d));
newSurf.DrawPixel(100 + FloatToInt(x), 100 + FloatToInt(y));
}
}
}
}
DrawingSurface *ds = Room.GetDrawingSurfaceForBackground();
ds.DrawImage(100, 50, newBg.Graphic);
newBg.Delete();
}
function room_Load()
{
Debug(4, 1);
unchangedBg = DynamicSprite.CreateFromBackground(0, 100, 50, 200, 200);
}
what a bastard, that's amazing, really nice idea on solving the thing with the circle from your previous post!
I was thinking, it might be the nested loops slowing it down. Maybe it will go better if you do one big loop for all pixels and use div and modulus to get the column and row indexes.
It is the iterations, Steve is doing way less of those.
Wait a minute, I think I've done a similar effect in Terror of the Vampire. You don't even need to use PutPixel, because DrawCircle and CopyTransparencyMask are much, much faster. It ends up looking like this:
(It's at 2:25)
Basically, you draw two circles over each other (one to draw the ring, the other to cut out the middle), take a screenshot of the current screen, and cut it out with the mask. Then you blit that translucently to the screen with maybe just 1 pixel of random jitter to throw everything off, and the next frame, before you even remove the first bit, you take another screenshot and do the same thing. It's crude, but it's FAST. Also I'm really regretting not commenting all my code, but this was for a MAGS game so I didn't have much time to document it.
// new module script
DynamicSprite *echosprite;
DynamicSprite *echosprite2;
DynamicSprite *echomask;
int angle = 1;
int echoX;
int echoY;
int echotimer=0;
bool doEcho = false;
int size;
static function VFX::EchoAt (int x, int y,int radius)
{
echoX = x;
echoY = y;
echotimer = 0;
doEcho = true;
size = radius*2;
echosprite = DynamicSprite.Create (radius*2, radius*2);
echosprite2 = DynamicSprite.CreateFromExistingSprite (echosprite.Graphic);
sldEcho.Min = 0;
sldEcho.Max = radius;
sldEcho.Value = 0;
sldEcho.TweenValue (1.0, radius, eEaseLinearTween, eNoBlockTween);
sldEcho2.Min = 0;
sldEcho2.Max = radius;
sldEcho2.Value = 0;
sldEcho2.TweenValue (1.2, radius, eEaseInOutSineTween, eNoBlockTween);
}
function MakeEcho ()
{
if (echotimer < size && doEcho)
{
echomask = DynamicSprite.Create (size, size);
DrawingSurface *surf = echomask.GetDrawingSurface ();
surf.DrawingColor = 15;
surf.DrawCircle (size/2, size/2, sldEcho.Value);
surf.DrawingColor = 0;
surf.DrawCircle (size/2, size/2, sldEcho2.Value);
surf.Release ();
echosprite2 = DynamicSprite.CreateFromExistingSprite (echosprite.Graphic);
echosprite = DynamicSprite.CreateFromScreenShot (320, 200);
int newX = echoX-(size/2);
int w=newX+size;
int underflowX;
int overflowX;
int newY = echoY-(size/2);
int h = newY+size;
int underflowY;
int overflowY;
if (newX < 0)
{
underflowX = newX * (-1);
newX = 0;
}
if (newX+size > 320)
{
overflowX = (newX+size-320);
w = 320-overflowX;
}
{
}
if (newY < 0)
{
underflowY = newY * (-1);
newY = 0;
}
if (newY+size > 200)
{
overflowY = (newY+size-200);
h = 200-overflowY;
}
if (w+newX > echosprite.Width)
{
newX = 0;
w = echosprite.Width;
}
if (h+newY > echosprite.Height)
{
newY = 0;
h = echosprite.Height;
}
echosprite.Crop (newX, newY, w, h);
echosprite.ChangeCanvasSize (size, size, overflowX-underflowX, overflowY-underflowY);
echosprite.CopyTransparencyMask (echomask.Graphic);
Translucence.CreateOverlay (80, echosprite.Graphic, 128, 2, echoX-(size/2)-1-Random(1), echoY-(size/2)+1-Random(1)); //You can replace this with drawing at 50% transparency.
//Translucence.CreateOverlay (81, echomask.Graphic, 128-echotimer, 2, echoX-(size/2), echoY-size/2);
echotimer = sldEcho.Value;
}
else
{
echotimer = 0;
echoX = 0;
echoY = 0;
doEcho = false;
Translucence.DeleteOverlay (80);
Translucence.DeleteOverlay (81);
}
}
Quote from: Dualnames on Sat 16/02/2019 20:46:06
what a bastard
I love you too, Dual!
Smart idea with the mask, Snarky & Scavenger! Kind of hard to tell with the video compression but it sounds like it would work well. (And lovely art all round!)
Well, my issues with my code is i wanted to do a rectangle then i thought why not a circle, then proceeded to circle the rectangle instead of drawing a circle lol, also wow that is almost the exact same effect!