i'm new to godot and am making a pong game to practice, i tried making an AI and it works but the movement is extremely clunky when the ball gets near the paddle
here's my code:
var direction: = Vector2(0.0,0.0)
var velocity:= Vector2(0.0,0.0)
Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass
Called every frame. 'delta' is the elapsed time since the previous frame.
func physicsprocess(delta: float) -> void:
position.x = 135
var ball = getparent().getnode("ball").position
if ball.y < position.y:
direction.y = -1
velocity = speed * direction
moveandslide(velocity)
elif ball.y > position.y:
direction.y = 1
velocity = speed * direction
moveandslide(velocity)```
what happens if you don't repeat the following code?
velocity = speed * direction
moveandslide(velocity)
Also, I'm assuming the speed of the paddle is faster than the speed of the ball, in which case you're going to get a stuttering effect when the paddle closes in on the ball and creates this loop:
paddle passes the ball
paddle changes direction
ball passes the paddle
paddle changes direction... and so on.
I haven't tested this but if you adjust your code slightly, this could fix the issue:
if ball.y < (position.y - speed): and elif ball.y > (position.y + speed)
As I say, I haven't tested this so you might have to change the signs but that should hopefully stop the jerkiness.
Related
I started to learn to code last year.Not very good though.
I tried to plot the trajectory of cannonball shot from the ground using c language. (I didn't code this alone anyway).
(# sorry I have to delete the original code for a while. If you would like to see my original code, please let me know. but I'm sure it'd be totally enough if you just see the selected answer below. )
and if I excute it, the result is like this.
the speed is?
6
the angle is
32
the spring is
0.4
X=0.000000, Y=0.000000
X=0.000000, Y=-0.049000
X=0.000000, Y=-0.049000
X=0.000000, Y=-0.049000
X=0.000000, Y=-0.049000
X=0.000000, Y=-0.049000
X=0.000000, Y=-0.049000
X=0.000000, Y=-0.049000
X=0.000000, Y=-0.049000
X=0.000000, Y=-0.049000
I tried to figure out what I did wrong, but I have no idea at all.
Please help me!
The problem with this approach is that the Y-coordinate can become negative. And once it does, if the initial speed is not high enough, the ball can become stuck underneath the surface, as demonstrated by #RetiredNinja's results (the coordinate becomes stuck at -0.049 at some point which is not the correct behavior).
Erroneous results (not to scale) from original code, with modified parameters:
The ball seems to "tunnel" through the ground (some values dip below zero).
How to resolve this? We need to resolve the collision properly, making sure that the ball bounces instead of crossing the surface boundary. To do so, let's examine the behavior of the ball during a timestep in which a bounce occurs.
A collision occurs when both the vertical velocity and vertical position are negative. To find the collision time and velocity, use the equations of motion:
Once we have the collision velocity, we can simply update the new vertical speed to -spring * vc (spring should have a better name, e.g. coef_rest); an additional advantage is that we no longer need calls to pow.
This may occur several times during a timestep, so we need to perform this in a loop. One more thing to watch out for is that as the vertical velocity decays, the bounces become infinitely more frequent - so we need a "cut-off" velocity at which to stop the ball from bouncing.
Code:
#include <stdio.h>
#include <math.h>
#ifndef M_PI
#define M_PI 3.141592654
#endif
int main()
{
const double g = 9.81; // gravity
const double dt = 0.025; // time step
const double maxtime = 5.0; // max time
const double spring = 0.95; // coefficient of restitution
const double cutoff = 1e-4; // cut-off velocity
double speed = 6;
double angle = 32;
angle = angle * M_PI / 180.0;
double init_vx = speed * cos(angle);
double init_vy = speed * sin(angle);
int springnumber = 0;
printf("0.0,0.0\n");
for (double ts = 0.0, vs = init_vy, time = dt; time <= maxtime;)
{
// positions *after* this time step
double px = time * init_vx;
double elapse = time - ts;
double py = 0.0, vy = 0.0;
if (vs >= cutoff)
{
py = (vs - 0.5 * g * elapse) * elapse;
vy = vs - g * elapse;
}
// check for bounce
if (vy < 0.0 && py < 0.0)
{
// collision time
double tc = 2.0 * vs / g;
// update speed after bounce and time of collision
springnumber++;
vs *= spring;
ts += tc;
continue;
}
// print
printf("%f,%f\n", px, py);
// timestep
time += dt;
}
return 0;
}
Test results, with the same parameters as before:
The ball no longer "tunnels", which is the correct behavior.
I'm a bit of an openGL/programming noobie so I'm trying to make an "AI" for the right paddle. I know this isnt the proper way of doing it, what I SHOULD be doing is making it follow the ball. But right now I'm just trying to make it perpetually move up and down. I can't figure out how to do it, trying to use If loops like
if (paddle.pos[1] > 1){
paddle.pos[1] = paddle.pos[1] - delta}
I set delta to something like 0.01, 1 is the top of the screen. Obviously this isnt right because as soon as it goes down below 1 it goes up again, but I'm trying to do something like it.
2nd question - How do you move the ball from 0,0 when it starts? Kind of the same problem, am using if statements with the x values but thats definitely not right.
This is using C by the way.
Try something like this to make pos repeatedly go from 0 to 1 and back to 0:
// Initialize.
float pos = 0.0f;
float delta = 0.01f;
// On every update.
pos += delta;
if (pos > 1.0f) {
pos = 1.0f;
delta = -delta;
} else if (pos < 0.0f) {
pos = 0.0f;
delta = -delta;
}
The key here is that you invert the sign of your increment each time you reach one of the end positions.
We all know how to draw a line in Processing.
But when we draw a line, the line is shown immediately.
What if i want to witness the drawing process, namely, to see the line moving forward, gradually completes a whole line.
Here's what i want to realize: to DRAW several lines and curves which finally turn into some pattern.
So how to make that happen? Using array?
Many thanks.
In processing all of the drawing happens in a loop. An easy way to create animated sequences like you describe is to use frameCount to drive it and using the modulus function % is a good way to create a loop. For example, to animate along the x axis:
void draw() {
float x = 50;
float y = 50;
float lineLength = 50;
int framesToAnimate = 60;
line(x,y,x+float(frameCount % framesToAnimate)/framesToAnimate*lineLength, y);
}
Note: strange things will happen if you don't cast / convert to a float
I use this pretty often to animate other features such as the color.
fill(color(127 + sin(float(frameCount)/90)*127, 0, 0, 127));
If you want to get more advanced, setting vectors and coordinates with PVector. There is a pretty good tutorial on Daniel Shiffman's site.
If you want to set your animation independent of frame rate, you can use mills() instead. That will return current time since the sketch started so you can set something to happen in a given time in seconds.
like for example:
long initialTime;
void setup(){
size(400,200);
initialTime = millis();
}
void draw() {
float x = 50;
float y = 50; //set the multiplier to adjust speed
line(x,y,x+(millis()-initialTime)*0.01, y); //10 px/sec
line(x,y+50,x+(millis()-initialTime)*0.05, y+50); //50 px/sec
line(x,y+100,x+(millis()-initialTime)*0.001, y+100); // 1 px/sec
}
There is also some animation libraries, i've seen some impressive results with some, but i never used them. Here a list.
I'm in the process of writing a little game to teach myself OpenGL rendering as it's one of the things I haven't tackled yet. I used SDL before and this same function, while still performing badly, didn't go as over the top as it does now.
Basically, there is not much going on in my game yet, just some basic movement and background drawing. When I switched to OpenGL, it appears as if it's way too fast. My frames per second exceed 2000 and this function uses up most of the processing power.
What is interesting is that the program in it's SDL version used 100% CPU but ran smoothly, while the OpenGL version uses only about 40% - 60% CPU but seems to tax my graphics card in such a way that my whole desktop becomes unresponsive. Bad.
It's not a too complex function, it renders a 1024x1024 background tile according to the player's X and Y coordinates to give the impression of movement while the player graphic itself stays locked in the center. Because it's a small tile for a bigger screen, I have to render it multiple times to stitch the tiles together for a full background. The two for loops in the code below iterate 12 times, combined, so I can see why this is ineffective when called 2000 times per second.
So to get to the point, this is the evil-doer:
void render_background(game_t *game)
{
int bgw;
int bgh;
int x, y;
glBindTexture(GL_TEXTURE_2D, game->art_background);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &bgw);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &bgh);
glBegin(GL_QUADS);
/*
* Start one background tile too early and end one too late
* so the player can not outrun the background
*/
for (x = -bgw; x < root->w + bgw; x += bgw)
{
for (y = -bgh; y < root->h + bgh; y += bgh)
{
/* Offsets */
int ox = x + (int)game->player->x % bgw;
int oy = y + (int)game->player->y % bgh;
/* Top Left */
glTexCoord2f(0, 0);
glVertex3f(ox, oy, 0);
/* Top Right */
glTexCoord2f(1, 0);
glVertex3f(ox + bgw, oy, 0);
/* Bottom Right */
glTexCoord2f(1, 1);
glVertex3f(ox + bgw, oy + bgh, 0);
/* Bottom Left */
glTexCoord2f(0, 1);
glVertex3f(ox, oy + bgh, 0);
}
}
glEnd();
}
If I artificially limit the speed by called SDL_Delay(1) in the game loop, I cut the FPS down to ~660 ± 20, I get no "performance overkill". But I doubt that is the correct way to go on about this.
For the sake of completion, these are my general rendering and game loop functions:
void game_main()
{
long current_ticks = 0;
long elapsed_ticks;
long last_ticks = SDL_GetTicks();
game_t game;
object_t player;
if (init_game(&game) != 0)
return;
init_player(&player);
game.player = &player;
/* game_init() */
while (!game.quit)
{
/* Update number of ticks since last loop */
current_ticks = SDL_GetTicks();
elapsed_ticks = current_ticks - last_ticks;
last_ticks = current_ticks;
game_handle_inputs(elapsed_ticks, &game);
game_update(elapsed_ticks, &game);
game_render(elapsed_ticks, &game);
/* Lagging stops if I enable this */
/* SDL_Delay(1); */
}
cleanup_game(&game);
return;
}
void game_render(long elapsed_ticks, game_t *game)
{
game->tick_counter += elapsed_ticks;
if (game->tick_counter >= 1000)
{
game->fps = game->frame_counter;
game->tick_counter = 0;
game->frame_counter = 0;
printf("FPS: %d\n", game->fps);
}
render_background(game);
render_objects(game);
SDL_GL_SwapBuffers();
game->frame_counter++;
return;
}
According to gprof profiling, even when I limit the execution with SDL_Delay(), it still spends about 50% of the time rendering my background.
Turn on VSYNC. That way you'll calculate graphics data exactly as fast as the display can present it to the user, and you won't waste CPU or GPU cycles calculating extra frames inbetween that will just be discarded because the monitor is still busy displaying a previous frame.
First of all, you don't need to render the tile x*y times - you can render it once for the entire area it should cover and use GL_REPEAT to have OpenGL cover the entire area with it. All you need to do is to compute the proper texture coordinates once, so that the tile doesn't get distorted (stretched). To make it appear to be moving, increase the texture coordinates by a small margin every frame.
Now down to limiting the speed. What you want to do is not to just plug a sleep() call in there, but measure the time it takes to render one complete frame:
function FrameCap (time_t desiredFrameTime, time_t actualFrameTime)
{
time_t delay = 1000 / desiredFrameTime;
if (desiredFrameTime > actualFrameTime)
sleep (desiredFrameTime - actualFrameTime); // there is a small imprecision here
}
time_t startTime = (time_t) SDL_GetTicks ();
// render frame
FrameCap ((time_t) SDL_GetTicks () - startTime);
There are ways to make this more precise (e.g. by using the performance counter functions on Windows 7, or using microsecond resolution on Linux), but I think you get the general idea. This approach also has the advantage of being driver independent and - unlike coupling to V-Sync - allowing an arbitrary frame rate.
At 2000 FPS it only takes 0.5 ms to render the entire frame. If you want to get 60 FPS then each frame should take about 16 ms. To do this, first render your frame (about 0.5 ms), then use SDL_Delay() to use up the rest of the 16 ms.
Also, if you are interested in profiling your code (which isn't needed if you are getting 2000 FPS!) then you may want to use High Resolution Timers. That way you could tell exactly how long any block of code takes, not just how much time your program spends in it.
I am working on a robot, it's part of the summer robotics workshop in our college. We are using C-STAMP micro controllers by A-WIT. I was able to make it move, turn left, turn right, move backward. I have even managed to make it go along the black tape using a contrast sensor.
I send the robot at 30-45 degrees toward the black tape on the table and it aligns itself and starts to move along the black tape. It jerks a little, probably due to my programming logic below, it's running a while-loop and constantly checking if-statements, so it ends up trying to turn left and right every few milliseconds, which explains the jerking part. But it's okay, it works, not as smooth as I want it to work but it works! Problem is that I can't make my robot go into a rectangular path of the black tape. As soon as it reaches the corner of the black tape it just keeps going straight instead of making left or right turn.
My 2 sensors are located right underneath the robot, next to the front wheel, almost at the floor level. It has "index" value ranging from 0 to 8. Eight being the brightest contrast and zero being the darkest contrast. So when the robot moves into the black-tape-zone, the index value drops, and based on that, I have an if-statement telling my robot to either turn left or right.
Here's my attempt. To avoid confusion I didn't post the entire source code, but only the logical part responsible for the movement of my robot along the black tape.
while(1) {
// don't worry about these.
// 10 and 9 represent Sensor's PIN location on the motherboard
V = ANALOGIN(10, 1, 0, 0, 0);
V2 = ANALOGIN(9, 1, 0, 0, 0);
// i got this "formula" from the example in my Manual.
// V stands for voltage of the sensor.
// it gives me the index value of the sensor. 0 = darkest, 8 = lightest.
index = ((-(V - 5) / 5) * 8 + 0.5);
index2 = ((-(V2 - 5) / 5) * 8 + 0.5);
// i've tweaked the position of the sensors so index > 7 is just right number.
// the robot will move anywhere on the table just fine with index > 7.
// as soon as it drops to or below 7 (i.e. finds black tape), the robot will
// either turn left or right and then go forward.
// lp & rp represent left-wheel pin and right-wheel pin, 1 means run forever.
// if i change it from 1 to 100, it will go forward for 100ms.
if (index > 7 && index2 > 7)
goForward(lp, rp, 1);
if (index <= 7) {
turnLeft(lp, rp, 1);
goForward(lp, rp, 1);
// this is the tricky part. i've added this code last minute
// trying to make my robot turn, but i didn't work.
if (index > 4) {
turnLeft(lp, rp, 1);
goForward(lp, rp, 1);
}
}
else if (index2 <= 7) {
turnRight(lp, rp, 1);
goForward(lp, rp, 1);
// this is also the last minute addition. it's same code as above
// but it's for the 2nd sensor.
if (index2 > 4) {
turnRight(lp, rp, 1);
goForward(lp, rp, 1);
}
}
}
I've spent the entire day trying to figure it out. I've pretty much exhausted all avenues. Asking for the solution on stackoverflow is my very last option now.
Thanks in advance!
If you have any questions about the code, let me know, but comments should be self-explanatory.
This is my goForward function in case anyone wonders:
void goForward(BYTE lp, BYTE rp, WORD t)
{
WORD i;
for(i = 0; i < t; i = i + 1){
PULSOUT(lp, 400, 1, 1);
PULSOUT(rp, 800, 1, 1);
PAUSE(17);
}
}
UPDATE: Here's what I've come up with so far. I've erased all my if-statements that I've posted earlier and decided to write the logic from scratch:
// if there's enough bright light in both sensors at the same time
// robot will move forward forever.
if (index > 7 && index2 > 7)
goForward(lp, rp, 1);
// but if there's not enough bright light anymore (i.e. reached black tape)
// proceed to the else-statement.
else {
// if left sensor detects the black tape then turn right
// if doesn't detect the black tape then keep going forward
if (index2 <= 7)
turnRight(lp, rp, 1);
else
goForward(lp, rp, 1);
// if right sensor detects the black tape then turn left
// if it doesn't detect the black tape then keep going forward
if (index <= 7)
turnLeft(lp, rp, 1);
else
goForward(lp, rp, 1);
}
// The reason for turnLeft and turnRight is to keep robot re-alligning
// to the black tape. It will happen so fast (every 1ms) that the jerking
// shouldn't even be noticeable.
You need to trap the sudden occurrence: [cycle n] "I see the tape" -> [cycle n+1] "I don't see the tape" that happens when you encounter a corner.
A state machine is a good way of handling this type of problem. With a state machine the code for a specific state is isolated, it only runs when the state is true. This isolation prevents "cross-talk" and gives you one known code block for each state.
Given your example the flow might be something like:
:Loop
State == Moving forward and on tape.
read sensors
delta = abs(valueLastCycle - valueThisCycle);
if (delta >= bigChangeValue){
// the tape was just gone.
// change state and handle this situation.
CurrentState = suddenValueChange;
}else
if (index <= 7) {
turnLeft(lp, rp, 1);
goForward(lp, rp, 1);
else if (index2 <= 7) {
turnRight(lp, rp, 1);
goForward(lp, rp, 1);
}
...
State == suddenValueChange
...
code that handles sudden value change event
maybe:
Stop Robot;
Move backwards slowly until on tape or distance x
etc...
Goto Loop
Increasing the scan rate might seem to help....but the faster the robot moves the faster your scan rate....i.e. You might still jump from on tape -> off tape in which case your current code flounders.
My guess is that the goForward immediately after the turnLeft/TurnRight "cancels" the turning?
It depends on how turning is implemented though
Make sure your "plan" makes sense before worrying about the code.
Start by moving the robot around by hand and observing when the sensors pass over black and white areas. Try to come up with a behaviour, and simulate it by hand. If this doesn't work as you want, then you need to revise the behaviour.
A suggestion: You may wish to add some more loops to ensure that if something seems wrong, the robot corrects itself before resuming normal behaviour. (E.g. rather than turning right/left for 100ms, do that behaviour as long as it takes for the sensors to see the right thing.)
Does your while loop run fast enough to catch a corner? If both sensors report that they are on the tape, and in the next cycle both sensors are off the tape, you can't detect that, right? The sensor reports the same values (8) for being on the tape and off the tape?
Try making your code faster. Without seeing the entire code, it's hard to make a recommendation, but it seems that you might be evaluating if statements that are not necessary. Add an ELSE after the first IF, once you know that you are going straight.
It seems that your goforward implementation is blocking the loop for too long. 1 does not mean run forever, it does one pass of the for-loop, and then you PAUSE 17 (msec?). What's the PAUSE for? Remove it. This probably causes the jerking, and it prevents the next set of sensor values from coming in.
Right now, by the looks of your code, every time the robot sees black, it goes forward forever. I'm not really sure how going forward is implemented.
In pseudocode, your code says:
if you see black
go forward forever
if you don't see black
turn left or right
Do you see how your robot would go forward forever with that kind of logic? Again, I have no idea how going forward is implemented so I could be wrong.