p5.js - get a rectangle to move left and right repeatedly (bounce) - if-statement

I'm trying out some sample code for a bigger project, and I'm having trouble getting my rectangle to bounce between two lines.
function draw() {
print(frameCount)
background(255)
var x = 150 + frameCount;
rect(x,200,15,15);
line(150,0,150,400);
line(250,0,250,400);
if (x >= 250) {
background(255)
x = 350-frameCount;
rect(x,200,15,15);
line(250,0,250,400);
line(150,0,150,400);
} if (x <= 145) {
background(255)
x = 145 + (frameCount % 100);
rect(x,200,15,15);
line(250,0,250,400);
line(150,0,150,400);
}
}
I'm getting the feeling that after the first instance, it's disregarding the original if statement, which dictates a bounce to the left. I'm really not sure what's going wrong, and any help would be appreciated.

You probably just want to store the current position and speed in a set of variables, and then move the rectangle based on those. Here's an example:
var x = 0;
var speed = 1;
function draw(){
x += speed;
if(x < 0 || x > width){
speed *= -1;
}
background(64);
line(x, 0, x, height);
}
I've written a tutorial on this available here. That's for regular Processing, but the ideas are the same in P5.js.

Related

Collision detection in voxel world

I am kinda stuck with my basic voxel physics right now. It's very, very choppy and I am pretty sure my maths is broken somewhere, but let's see what you have to say:
// SOMEWHERE AT CLASS LEVEL (so not being reinstantiated every frame, but persisted instead!)
glm::vec3 oldPos;
// ACTUAL IMPL
glm::vec3 distanceToGravityCenter =
this->entity->getPosition() -
((this->entity->getPosition() - gravityCenter) * 0.005d); // TODO multiply by time
if (!entity->grounded) {
glm::vec3 entityPosition = entity->getPosition();
if (getBlock(floorf(entityPosition.x), floorf(entityPosition.y), floorf(entityPosition.z))) {
glm::vec3 dir = entityPosition - oldPos; // Actually no need to normalize as we check for lesser, bigger or equal to 0
std::cout << "falling dir: " << glm::to_string(dir) << std::endl;
// Calculate offset (where to put after hit)
int x = dir.x;
int y = dir.y;
int z = dir.z;
if (dir.x >= 0) {
x = -1;
} else if (dir.x < 0) {
x = 1;
}
if (dir.y >= 0) {
y = -1;
} else if (dir.y < 0) {
y = 1;
}
if (dir.z >= 0) {
z = -1;
} else if (dir.z < 0) {
z = 1;
}
glm::vec3 newPos = oldPos + glm::vec3(x, y, z);
this->entity->setPosition(newPos);
entity->grounded = true; // If some update happens, grounded needs to be changed
} else {
oldPos = entity->getPosition();
this->entity->setPosition(distanceToGravityCenter);
}
}
Basic idea was to determine from which direction entityt would hit the surface and then just position it one "unit" back into that direction. But obviously I am doing something wrong as that will always move entity back to the point where it came from, effectively holding it at the spawn point.
Also this could probably be much easier and I am overthinking it.
As #CompuChip already pointed out, your ifs could be further simplified.
But what is more important is one logical issue that would explain the "choppiness" you describe (Sadly you did not provide any footage, so this is my best guess)
From the code you posted:
First you check if entity is grounded. If so you continue with checking if there is a collision and lastly, if there is not, you set the position.
You have to invert that a bit.
Save old position
Check if grounded
Set the position already to the new one!
Do collision detection
Reset to old position IF you registered a collision!
So basically:
glm::vec3 distanceToGravityCenter =
this->entity->getPosition() -
((this->entity->getPosition() - gravityCenter) * 0.005d); // TODO multiply by time
oldPos = entity->getPosition(); // 1.
if (!entity->grounded) { // 2.
this->fallingStar->setPosition(distanceToGravityPoint); // 3
glm::vec3 entityPosition = entity->getPosition();
if (getBlock(floorf(entityPosition.x), floorf(entityPosition.y), floorf(entityPosition.z))) { // 4, 5
this->entity->setPosition(oldPos);
entity->grounded = true; // If some update happens, grounded needs to be changed
}
}
This should get you started :)
I want to elaborate a bit more:
If you check for collision first and then set position you create an "infinite loop" upon first collision/hit as you collide, then if there is a collision (which there is) you set back to the old position. Basically just mathematic inaccuracy will make you move, as on every check you are set back to the old position.
Consider the if-statements for one of your coordinates:
if (dir.x >= 0) {
x = -1;
}
if (dir.x < 0) {
x = 1;
}
Suppose that dir.x < 0. Then you will skip the first if, enter the second, and x will be set to 1.
If dir.x >= 0, you will enter the first if and x will be set to -1. Now x < 0 is true, so you will enter the second if as well, and x gets set to 1 again.
Probably what you want is to either set x to 1 or to -1, depending on dir.x. You should only execute the second if when the first one was not entered, so you need an else if:
if (dir.x >= 0) {
x = -1;
} else if (dir.x < 0) {
x = 1;
}
which can be condensed, if you so please, into
x = (dir.x >= 0) ? -1 : 1;

Manipulating a diagonal line in C++

I am playing with using nested for-loops to plot pixels and basically draw flags. So far I've figured out how to make circles, diagonal lines and crosses.
I am however not able to wrap my head around how to limit from where a straight line is to be drawn.
Basically I'm trying to figure out how I need to change the code I used to draw the diagonal lines in the union jack to make the swastika in the flag of nazi Germany. Any help would be much appreciated!
Here's my current code and a screenshot of what I get:
for (int x = 0; x < 240; x++)
{
for (int y = 0; y < 160; y++)
{
uint16_t cX = 120;
uint16_t cY = 80;
uint16_t r = 66;
// Makes line
if (x-100 < y * 240 / 240 + 20 && x-100 > y * 240 / 240 - 20)
{
PlotPixel16(x, y, black);
}
// Makes circle
else if (((x-cX)*(x-cX))+((y-cY)*(y-cY)) < r*r)
{
PlotPixel16(x, y, white);
}
else
{
PlotPixel16(x, y, red);
}
}
}
Screenshot!
You're actually drawing a polygon defined by four lines. The equation of a line is y=mx+b, and you want to be either above or below the line. While this isn't how I would do it, it's keeping with the spirit of your approach to test that y-mx+b<0 (or >0) for four different pairs of m and b. That will give you one line segment, and you can get the rest similarly.
Right now, you're selecting a region between only two lines. That's why you're getting that image.

Graphing issues in data output

I have a program that is taking input from an external source (right now, a joystick), and then plotting it on a graph. The graph displays the last 60 frames of data, which is about 1-2 seconds.
here is the data input:
nextDataPoint(double x){
if (x > max){ max = x; }
if (x < min){ min = x; }
dataInput.enqueue(x) //dataInput is a QQueue<double>
while (dataInput.size() > 60){
dataInput.dequeue();
}
update(); //this triggers the paint event
}
here is the graphing function
graph function:
//this draws the min line and the max line
QPainter painter(this);
int lineDist = 25;
QPen myPen(Qt::black, 3);
QPoint maxText(10,20);
painter.drawText(maxText, "max");
QPoint maxLineLeft(0, lineDist);
QPoint maxLineRight(width(), lineDist);
painter.drawLine(maxLineLeft, maxLineRight);
QPoint minText(10, height()-10);
painter.drawText(minText, "min");
QPoint minLineLeft(0, height()-lineDist);
QPoint minLineRight(width(), height()-lineDist);
//this draws the actual graph
myPen.setColor(Qt::blue);
myPen.setWidth(2);
painter.setPen(myPen);
double dist = (double)(heigh() - 2*lineDist);
int stepSize = (int)((double)width() / 60.0);
int heightStep = (max-min)/dist;
double x;
QPoint lastPoint(0,0);
QPoint nextPoint(0,0);
int i = 0;
if (!dataInput.empty()){ //checks that there is data
if (dataInput.size() < 60) { //ignoring for sake of brevety
} else {
x = dataInput.at(i);
x = max - x; // this inverts the data, necessary because (0,0) is the upper left corner
x = (x-min)/heightStep;
nextPoint.setX(0);
nextPoint.setY(x+lineDist);
for (i = 1; i < 60; i++){
x = dataInput.at(i);
x = max - x;
x = (x-min)/heightStep;
lastPoint = nextPoint;
nextPoint.setX(i*stepSize);
nextPoint.setY(x+lineDist);
painter.drawLine(lastPoint, nextPoint);
}
}
}
So, with one joystick that I am testing with, this program works without issue. As I move the joystick, the program draws the blue line within the bounds listed on the screen. x is input as a number between 0 and 65535, and after moving the joystick around a little bit the system recognizes that as the max and the min, and then proceeds to function as expected.
The second "joystick" is less of a joystick and more of a pressure sensor. This outputs either x = 1023 or x is some number between 5 and 9. In this case however, the blue line appears on the same level as the max line, or when the data output is x in the single digits, the blue line appears below min. I haven't been able to figure out why this would be the case, the first joystick can also produce results of 0, but it never goes below the minimum line. What might be causing this particular issue?
Two changes were needed to correct the issues.
First, stepSize and heightStep were both changed from int to double.
second, the section
x = dataInput.at(i);
x = max - x;
x = (x-min)/heightStep;
nextPoint.setX(0);
nextPoint.setY(x+lineDist);
for (i = 1; i < 60; i++){
x = dataInput.at(i);
x = max - x;
x = (x-min)/heightStep;
lastPoint = nextPoint;
nextPoint.setX(i*stepSize);
nextPoint.setY(x+lineDist);
painter.drawLine(lastPoint, nextPoint);
}
was changed to:
x = dataInput.at(i);
x = (x-min)/heightStep;
nextPoint.setX(0);
nextPoint.setY(dist-x+lineDist);
for (i = 1; i < 60; i++){
x = dataInput.at(i);
x = (x-min)/heightStep;
lastPoint = nextPoint;
nextPoint.setX(i*stepSize);
nextPoint.setY(dist-x+lineDist);
painter.drawLine(lastPoint, nextPoint);
}
This solved the issue and made the system always draw the data between the min and max lines.

C++ SDL collision between sprites

After 30 minutes Googling, all I could find is this:
http://www.sdltutorials.com/sdl-collision
I thought the title was misleading, then I noticed it's just a nightmare of a way to detect collision between two sprites. All I want is to check whenever my player sprite touches something else (another sprite). How can I accomplish this?
I read there is a library called SDL_collision.h, but it's either in Pascal or empty.
Chances are you'd use SDL_Rect for your bounding box. Where x and y are the positions of your sprite, and w and h are the width and height of your sprite. Then all you need to do is use SDL_HasIntersection.
Here is a simple example:
SDL_Surface *Srfc1, *Srfc2;
Srfc1= IMG_Load("foo.png");
Srfc2= Srfc1;
Srfc2->refcount++;
SDL_Rect box1, box2;
box1.x = box1.y = 0;
box2.x = box2.y = 100;
box1.w = box2.w = Srfc1->w;
box2.h = box2.h = Srfc1->h;
// ... somewhere in your event handling logic
if (SDL_HasIntersection(&box1, &box2))
{
printf("Collision.");
}
// ...
SDL_FreeSurface(Srfc1);
SDL_FreeSurface(Srfc2);
Since you do not have SDL_HasIntersection, here's a quick little function that will suit your needs:
bool IntersectRect(const SDL_Rect * r1, const SDL_Rect * r2)
{
return !(r2->x > (r1->x + r1->w) ||
(r2->x + r2->w) < r1->x ||
r2->y > (r1->y + r1->h) ||
(r2->y + r2->h) < r1->y);
);
}
For reference the logic is:
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
Where 'right' and 'bottom' refer to 'x + width' and 'y + height' respectively. Use this to fix the function incase I made a typo.

Smoothing Land Around Lakes

Sorry if that title isn't very descriptive. Anyway, I am working on something dealing with randomly generating landscapes. I made lakes, but due to how they are make, they often cause straight edges / dropoffs, which aren't desirable. I am trying to smooth it (right after making the lake, if possible), by defining a max variation amount (so land heights cannot vary more than it), and have it fix land if it varies to much, and quit if it is fine.
The problem:
My attempted fix:
As you can see... it didn't work. It also occurs to me, I think it would be broken if it had to move down, although that case shouldn't actually occur, because lakes only ever sink the landscape. Anyway, here is the source of my attempt:
//smoothing land nearby
int maxVariation = 2; //similar to the max height variation when the land is generated
//going right
for (int xPos = rightBound + 1, previousHeight = 0; ; ++xPos)
{
if (previousHeight == 0)
for (; previousHeight < size.y; ++previousHeight)
if (grid[0][rightBound][previousHeight] != BlockColor::DIRT && grid[0][rightBound][previousHeight] != BlockColor::GRASS)
{
--previousHeight;
break;
}
for (int y = 0; y < size.y; ++y)
if (grid[0][xPos][y] == BlockColor::WATER)
goto done_smoothing_right;
int height;
for (height = 0; height < size.y; ++height)
if (grid[0][xPos][height] != BlockColor::DIRT && grid[0][xPos][height] != BlockColor::GRASS)
{
--height;
break;
}
int difference = std::abs(height - previousHeight);
previousHeight = height;
if (difference > maxVariation)
{
for (int j = 0; j < size.y; ++j)
{
int toMove = difference;
while (j + toMove >= size.y)
--toMove;
grid[0][xPos][j] = grid[0][xPos][j + toMove];
}
}
else
goto done_smoothing_right;
}
done_smoothing_right:
int tomakegotowork;
Note that is only the right side, but left should be about the same. How can I do this correctly?
Thanks if you can help.
EDIT:
I never did solve this problem. Instead, I made a recursive function to measure air, (from a certain height), and if a pocket of air (formed by the land) had enough, to fill with water. This has the advantaged of the land looking smooth because it is not altered.
This is written in java so you will need to convert it to c++ but it should give you the basic idea. It will only work for smoothing upwards as well and I only did the right side of the lake but it is very easy to modify it for the left side of the lake. I tried to match what I think the functionality of your code is.
Hope it helps...
void smoothLakeRight(Lake lake){
int x = lake.rightBound+1;
if(getGrassHeight(x)-lake.height>WorldConstants.MAX_LAKESIDE_VARIATION){
//if the right bank is too high start smoothing
int y =lake.height+WorldConstants.MAX_LAKESIDE_VARIATION;
while(grid[0][x][y] == BlockColor.DIRT){
fixGrass(x++, y++);
}
}
}
private int getGrassHeight(int xPos){
int y = WorldConstants.LOWEST_GRASS;
while(grid[0][xPos][y++] != BlockColor.GRASS);
return y-1;
}
private void fixGrass(int xPos, int yPos){
grid[0][xPos][yPos] = BlockColor.GRASS;
aboveAir(xPos,yPos);
belowDirt(xPos, yPos);
}
private void aboveAir(int xPos, int yPos) {
while(grid[0][xPos][++yPos]!=BlockColor.AIR){
if(grid[0][xPos][yPos]==BlockColor.TREE){
upRootTree(xPos, yPos);
}else{
grid[0][xPos][yPos]=BlockColor.AIR;
}
}
}
private void upRootTree(int xPos, int yPos) {
while(grid[0][xPos][yPos]==BlockColor.TREE){//remove stump
grid[0][xPos][yPos++]=BlockColor.AIR;
}
//remove leaves
grid[0][xPos][yPos] = BlockColor.AIR;
grid[0][xPos+1][yPos] = BlockColor.AIR;
grid[0][xPos-1][yPos] = BlockColor.AIR;
grid[0][xPos+1][yPos-1] = BlockColor.AIR;
grid[0][xPos-1][yPos-1] = BlockColor.AIR;
}
private void belowDirt(int xPos, int yPos) {
while(grid[0][xPos][--yPos]!=BlockColor.DIRT){
grid[0][xPos][yPos] = BlockColor.DIRT;
}
}