How to fix: code not running on certain frames - gml

I have been trying to make my character shoot a projectile on a particular frame of animation. However, sometimes it works and other times it just ignores creating the projectile.
I've tried using alarms instead of checking for the image index but I can't get the timer low enough to get the perfect timing.
I think it may be a problem with the image speed being 0.2 instead of 1.
I'm using a state machine to make it switch between moving and shooting, but I checked and it isn't a problem with state switching over as it changes when I want it to.
Here is relevant code from the shooting state:
if image_index == 2 {
instance_create(x+20*image_xscale,y,obj_projectile);
}
Here is the code that changes the tank over to the shooting state from the main state:
if key_shoot{
state = states.shoot;
image_speed = 0.2;
sprite_index = spr_tankShoot;
}
There is also an animation end event in the object with the following code:
if sprite_index == spr_tankShoot{
state = states.normal;
}
If anyone can see something wrong with the code and/or know what might be going wrong with this, it'd be much appreciated.

I think it may be a problem with the image speed being 0.2 instead of 1.
This is possible - if your animations have different speeds and you don't tend to reset image_index on animation start, you may end up with varying starting indexes (suppose, 0.1) that would not fall right on 2.0 when adding 0.2 to them. Checking that a frame is precisely a number is a not-as-good practice in general though.
You could store image_index at the end of the frame for future reference,
image_index_previous = image_index;
and then check that image_index stepped over 2 since the last frame:
if image_index_previous < 2 && image_index >= 2 {
instance_create(x+20*image_xscale,y,obj_projectile);
}

Related

Implementing real time plot with Qt5 charts

I am new to Qt and trying to implement a real time plot using QSplineSeries with Qt 5.7. I need to scroll the x axis as new data comes in every 100ms. It seems the CPU usage reaches 100% if I do not purge the old data which was appended to the series, using graphSeriesX->remove(0). I found two ways of scrolling the x axis.
const uint8_t X_RANGE_COUNT = 50;
const uint8_t X_RANGE_MAX = X_RANGE_COUNT - 1;
qreal y = (axisX->max() - axisX->min()) / axisX->tickCount();
m_x += y;
if (m_x > axisX->max()) {
axisX->setMax(m_x);
axisX->setMin(m_x - 100);
}
if (graphSeries1->count() > X_RANGE_COUNT) {
graphSeries1->remove(0);
graphSeries2->remove(0);
graphSeries3->remove(0);
}
The problem with the above is that m_x is of type qreal and at some time if I keep the demo running continuously, it will reach it's MAX value and the axisX->setMax call will fail making the plot not work anymore. What would be the correct way to fix this use case?
qreal x = plotArea().width() / X_RANGE_MAX;
chart->scroll(x, 0)
if (graphSeries1->count() > X_RANGE_COUNT) {
graphSeries1->remove(0);
graphSeries2->remove(0);
graphSeries3->remove(0);
}
However it's not clear to me how can I use the graphSeriesX->remove(0) call in this scenario. The graph will keep getting wiped out since once the series get appended with X_RANGE_COUNT values, the if block will always be true removing 0th value but the scroll somehow does not work the way manually setting maximum for x axis works and after a while I have no graph. scroll works if I do not call remove but then my CPU usage reaches 100%.
Can someone point me in the right direction on how to use scroll while using remove to keep the CPU usage low?
It seems like the best way to update data for a QChart is through void QXYSeries::replace(QVector<QPointF> points). From the documentation, it's much faster than clearing all the data (and don't forget to use a vector instead of a list). The audio example from the documentation does exactly that. Updating the axes with setMin, setMax and setRange all seem to use a lot of CPU. I'll try to see if there's a way around that.
What do you mean by "does not work the way manually setting maximum for x axis works"? The Second method you have shown works if you define x-axis range to be between 0 and X_RANGE_MAX. Is this not what you are after?
Something like: chart->axisX()->setRange(0, X_RANGE_MAX);

Stopping nodes to exact positions

I have a node moving on the screen with a velocity of 400pps. I would like to stop the node when it reaches a precise location on the screen. I am doing so by setting the node velocity to 0 in the update cycle.
However, the node moves pass the desired point by an unpredictable amount, and stops somewhere later.
I suspect the problem is in the response time. Simply put, the physic engine is not quick enough to stop the node accurately. My app is running at 30fps on my Mac and 60fps on iPhone 5S.
What is the best way to achieve accurate node movements and stopping? I suppose I could reposition the node to the desired location after I stop it, but that would probably mess up the all physic world.
This is how the relevant part of my update method looks like:
- (void)update:(NSTimeInterval)currentTime {
// Start moving
if(verticalScrollSpeed) {
node.physicsBody.velocity = CGVectorMake(0, verticalScrollSpeed);
}
// Stop moving
if(verticalScrollSpeed > 0 && obstaclesContainerNode.position.y >= 0) {
verticalScrollSpeed = 0;
node.physicsBody.velocity = CGVectorMake(0, 0);
}
}
You would have to cheat a little. Nothing is really "moving" in a game. everything is just kinda teleporting however many pixels per frame. If something is moving extremely fast you may overshoot. The best you can do is something like this
if distanceToTarget <= 1 {
yourNode.position = targetNode.position
}
The other thing you can consider is slowing down your node as it gets closer to the other one. That would make arrival more accurate.. but that may or may not be something you want for your game.

Variable Jump Height

I have been having great difficulty creating a jumping system whereby the user can tap the jump button for a small jump and hold it down for a higher jump.
I stumbled upon this topic:
https://gamedev.stackexchange.com/questions/13277/variable-height-jumping-in-side-scrollers
Which greatly helped me develop the following code:
PlayerMovementTimer = [NSTimer scheduledTimerWithTimeInterval:0.005 target:self selector:#selector(movePlayer) userInfo:nil repeats:YES];
[JumpButton addTarget:self action:#selector(jumpPlayer:) forControlEvents:UIControlEventTouchDown];
[JumpButton addTarget:self action:#selector(stopJump:) forControlEvents:UIControlEventTouchCancel | UIControlEventTouchUpInside | UIControlEventTouchDragExit];
- (void)movePlayer
{
CGFloat playerY = Player.center.y + PlayerYV;
if(playerY > 264) {
PlayerYV = 0;
playerY = 264;
}
if(playerY < 264) {
PlayerYV += 0.048f - PlayerYD;
}
if(HoldingJump && PlayerYV < 0 && PlayerYD + 0.0018f < 0.048f) {
PlayerYD += 0.0018f;
}
Player.center = CGPointMake(Player.center.x + PlayerXV, playerY);
}
- (IBAction)jumpPlayer:(id)sender
{
if(Player.center.y == 264) {
PlayerYD = 0;
PlayerYV = -2.25;
HoldingJump = true;
}
}
- (IBAction)stopJump:(id)sender
{
HoldingJump = false;
}
The code seems to work (some of the values need a bit of fine tuning but I haven't gotten round to that yet). The only problem is that the movement appears to be slightly jerky (even on the real device) and that when the player is at the top of the jump they accelerate really slowly and no values I put seem to be able to get the jump to look smooth like on Mario games.
Please take a look at the code and see if I am missing something obvious, or if there is a more efficient method of controlling movement than an NSTimer calling a void function. Also, is setting a UIImageView's position to a float value bad?
Thanks.
So there are quite a few things wrong here. First, yes, you should never be setting the origin of an ImageView or any other UI element to a coordinate position that is a fractional pixel. This causes sub-pixelling which will blur your image. To avoid this, all CGFloats should be rounded to the nearest whole number using roundf() or other similar rounding functions.
Another issue I can see is that you're setting Player.center. I hope for your sake that Player is not an ImageView cause you're going to be making your life harder. As mentioned above, when the origin of a frame is not set to a CGFloat that is a round number, you'll get sub-pixelling. When you use the center property, you can easily cause yourself to get on a bad origin value. For example, if I have a 11 by 11 image and set it's center to (11,11), the origin will get set to (5.5,5.5) and will cause sub-pixelling. Easy ways to avoid this is just do the math to place the origin correctly and make sure to round the CGFloats that you feed into it (or use CGRectIntegral on the frame before you set it).
A third issue here is that the timer is being called 0.005 seconds. Let's assume you want this game to run with 60 FPS. 60 FPS translates to about 0.0167 seconds. The timer is calling the method three times more often then it would need to even if you wanted 60 FPS and additionally, calling this method so often could be causing some of your jerky motion.
Now in terms of getting a "Mario" like jump, what you really need to do is look at getting a dedicated physics engine since if you're using the code above, you don't appear to have one. What a physics engine would do is it would apply a constant "gravity" which will help make the player jumps look and act more realistically. You would, when a player presses a button, apply an impulse up on the player character. The use of impulses would also simplify your work as you could apply impulses in different ways depending on how long they hold the button, etc. The code above is simply trying to get around this problem instead of addressing the real issue of you not having a physics engine.
Go investigate cocos2D and Box2D as a possible physics engine you could use. There are a wealth of resources on cocos2D+Box2D and there is a developer who even has made a tutorial on using cocos2D to create a Super Mario clone that should give you some basic understanding of how physics engines work: http://www.raywenderlich.com/15230/how-to-make-a-platform-game-like-super-mario-brothers-part-1

iOS Pong Development, Collision Detection

I am in a late phase of finishing my first usable iOS app.
I am creating a simple Pong game i am using a simple collision detection using CGRectIntersectsRect, but i came up with a problem.
if(CGRectIntersectsRect(mic.frame,plosina_a.frame)) {
if(mic.center.y < (plosina_a.center.y + plosina_a.frame.size.height)) {
RychlostMice.y = -RychlostMice.y;
}
}
if(CGRectIntersectsRect(mic.frame,plosina_b.frame)) {
if(mic.center.y < (plosina_b.center.y + plosina_b.frame.size.height)) {
RychlostMice.y = -RychlostMice.y;
}
}
When i use it like this the ball (mic) sort of gets to the paddles (plosina) and starts moving the other way sort of in the middle of the paddle.
My programming teacher managed to fix this problem for one of the paddles (the _b one) by adding the .frame.size.height instead of just .frame which i have used before, but when i did the same thing for the other paddle it didn't work i don't know what's up with that.
Also it creates another problem sometimes there is a situation where the ball get's caught up in the paddle - so I'm looking for a definition of the whole object and not just one side probably?
i hope you can help.
I can see three potential problems here.
The first is that you are waiting until the ball overlaps the paddle before counting it as a touch. It sounds like you really want to start the ball moving in the other direction when the ball touches the paddle, not when it intersects it. The CGRectIntersectsRect waits until they overlap before returning true. If you make either rectangle one pixel larger with a call to CGRectInset, your test will return true as soon as the ball reaches that paddle--by that time, there will be one pixel overlapping the expand rectangle. The test would look like this:
if(CGRectIntersectsRect(CGRectInset(mic.frame, -1, -1),plosina_a.frame)) {
if(mic.center.y < (plosina_a.center.y + plosina_a.frame.size.height)) {
RychlostMice.y = -RychlostMice.y;
}
}
if(CGRectIntersectsRect(CGRectInset(mic.frame, -1, -1),plosina_b.frame)) {
if(mic.center.y < (plosina_b.center.y + plosina_b.frame.size.height)) {
RychlostMice.y = -RychlostMice.y;
}
}
The second potential problem has to do with the velocity of the ball. Without seeing all of the code, I don't know if this is a problem or not. If the ball can move more than one pixel at a time, it could easily overlap--or even pass through--the paddle without a hit detection. There are lots of logic changes you can add to take care of this, but the easiest solution may be to just make sure the ball doesn't move more than one pixel at a time.
Finally, if you want to use the hack for both paddles, reverse the sign of the comparison on the other side of the game.
I'm guessing that your pong is played vertically, so that one paddle is at the top of the screen and the other is at the bottom?
If you you need to mirror the logic vertically for the other paddle. Right now you are using the same logic for the top and bottom paddles, but for the bottom paddle you probably need something like the following instead
if(CGRectIntersectsRect(mic.frame,plosina_b.frame)) {
if(mic.center.y > (plosina_b.center.y - plosina_b.frame.size.height)) {
RychlostMice.y = -RychlostMice.y;
}
}
Notice how I'm using the > sign and subtracting the height instead of adding it.

Bouncing ball not conforming to Conservation of Energy Rule

I am currently busy on writing a small ball physics engine for my programming course in Win32 API and c++. I have finished the GDI backbuffer renderer and the whole GUI (couple of more things to adjust) but i am very near to completion. The only big obstacles that last are ball to ball collision (but i can fix this on my own) but the biggest problem of them all is the bouncing of the balls. What happens is that i throw a ball and it really falls, but once it bounces it will bounce higher than the point were i released it??? the funny thing is, it only happens if below a certain height. This part is the physics code:
(If you need any more code or explanation, please ask, but i would greatly appreciate it if you guys could have a look at my code.)
#void RunPhysics(OPTIONS &o, vector<BALL*> &b)
{
UINT simspeed = o.iSimSpeed;
DOUBLE DT; //Delta T
BOOL bounce; //for playing sound
DT= 1/o.REFRESH;
for(UINT i=0; i<b.size(); i++)
{
for(UINT k=0; k<simspeed; k++)
{
bounce=false;
//handle the X bounce
if( b.at(i)->rBall.left <= 0 && b.at(i)->dVelocityX < 0 ) //ball bounces against the left wall
{
b.at(i)->dVelocityX = b.at(i)->dVelocityX * -1 * b.at(i)->dBounceCof;
bounce=true;
}
else if( b.at(i)->rBall.right >= SCREEN_WIDTH && b.at(i)->dVelocityX > 0) //ball bounces against the right wall
{
b.at(i)->dVelocityX = b.at(i)->dVelocityX * -1 * b.at(i)->dBounceCof;
bounce=true;
}
//handle the Y bounce
if( b.at(i)->rBall.bottom >= SCREEN_HEIGHT && b.at(i)->dVelocityY > 0 ) //ball bounces against the left wall
{
//damping of the ball
if(b.at(i)->dVelocityY < 2+o.dGravity/o.REFRESH)
{
b.at(i)->dVelocityY = 0;
}
//decrease the Velocity of the ball according to the bouncecof
b.at(i)->dVelocityY = b.at(i)->dVelocityY * -1*b.at(i)->dBounceCof;
b.at(i)->dVelocityX = b.at(i)->dVelocityX * b.at(i)->dBounceCof;
bounce=true;
}
//gravity
b.at(i)->dVelocityY += (o.dGravity)/o.REFRESH;
b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER;
//METER IS DEFINED GLOBALLY AS 100 which is the amount of pixels in a meter
b.at(i)->pOrigin.x += b.at(i)->dVelocityX/o.REFRESH*METER;
b.at(i)->UpdateRect();
}
}
return;
}
You are using the Euler method of integration. It is possible that your time step (DT) is too large. Also there seems to be a mistake on the row that updates the Y coordinate:
b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER;
You have already added the gravity to the velocity, so you don't need to add it to the position and you are not multiplying the velocity by DT. It should be like this:
b.at(i)->pOrigin.y += b.at(i)->dVelocityY * DT;
Furthermore there appears to be some confusion regarding the units (the way METER is used).
Okay, a few things here.
You have differing code paths for bounce against left wall and against right wall, but the code is the same. Combine those code paths, since the code is the same.
As to your basic problem: I suspect that your problem stems from the fact that you apply the gravity after you apply any damping forces / bounce forces.
When do you call RunPhysics? In a timer loop? This code is just an approximation and no exact calculation. In the short interval of delta t, the ball has already changed his position and velocity a litte bit which isn't considered in your algorithm and produces little mistakes. You'll have to compute the time until the ball hits the ground and predict the changes.
And the gravity is already included in the velocity, so don't add it twice here:
b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER;
By the way: Save b.at(i) in a temporary variable, so you don't have to recompute it in every line.
Ball* CurrentBall = b.at(i);
ANSWER!!ANSWER!!ANSWER!! but i forgot my other account so i can't flag it :-(
Thanks for all the great replies, it really helped me alot! The answers that you gave were indeed correct, a couple of my formulas were wrong and some code optimisation could be done, but none was really a solution to the problem. So i just sat down with a piece of paper and started calculation every value i got from my program by hand, took me like two hours :O But i did find the solution to my problem:
The problem is that as i update my velocity (whith corrected code) i get a decimal value, no problem at all. Later i increase the position in Y by adding the velocity times the Delta T, which is a verry small value. The result is a verry small value that needs to be added. The problem is now that if you draw a Elipse() in Win32 the point is a LONG and so all the decimal values are lost. That means that only after a verry long period, when the values velocity starts to come out of the decimal values something happens, and that alongside with that, the higher you drop the ball the better the results (one of my symptons) The solution to this problem was really simple, ad an extra DOUBLE value to my Ball class which contained the true position (including decimals) of my ball. During the RenderFrame() you just take the floor or ceiling value of the double to draw the elipse but for all the calculations you use the Double value. Once again thanks alot for all your replies, STACKOVERFLOW PEOPLE ROCK!!!
If your dBounceCof is > 1 then, yes your ball will bounce higher.
We do not have all the values to be able to reply to your question.
I don't think your equation for position is right:
b.at(i)->dVelocityY += (o.dGravity)/o.REFRESH;
This is v=v0+gt - that seems fine, although I'd write dGravity*DT instead of dGravity/REFRESH_FREQ.
b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER;
But this seems off: It is eqivalent to p = p0+v + 1/2gt^2.
You ought to multiply velocity * time to get the units right
You are scaling the gravity term by pixels/meter, but not the velocity term. So that ought to be multiplied by METER also
You have already accounted for the effect of gravity when you updated velocity, so you don't need to add the gravity term again.
Thanks for the quick replies!!! Sorry, i should have been more clear, the RunPhysics is beiing run after a PeekMessage. I have also added a frame limiter which makes sure that no more calculations are done per second than the refresh rate of the monitor. My dleta t is therefore 1 second devided by the refresh rate. Maybe my DT is actually too small to calculate, although it's a double value??? My cof of restitution is adjustable but starts at 0.9
You need to recompute your position on bounce, to make sure you bounce from the correct place on the wall.
I.e. resolve the exact point in time when the bounce occured, and calculate new velocity/position based on that direction change (partially into a "frame" of calculation) to make sure your ball does not move "beyond" the walls, more and more on each bounce.
W.r.t. time step, you might want to check out my answer here.
In a rigid body simulation, you need to run the integration up to the instant of collision, then adjust the velocities to avoid penetration at the collision, and then resume the integration. It's sort of an instantaneous kludge to cover the fact that rigid bodies are an approximation. (A real ball deforms during a collision. That's hard to model, and it's unnecessary for most purposes.)
You're combining these two steps (integrating the forces and resolving the collisions). For a simple simulation like you've shown, it's probably enough to skip the gravity bit on any iteration where you've handled a vertical bounce.
In a more advanced simulation, you'd split any interval (dt) that contains a collision at the actual instance of collision. Integrate up to the collision, then resolve the collision (by adjusting the velocity), and then integrate for the rest of the interval. But this looks like overkill for your situation.