I am new to c++ and I have been practicing collision in a small game program that does nothing and I just can't get the collision right
So I use images loaded into variables
background = oslLoadImageFile("background.png", OSL_IN_RAM, OSL_PF_5551);
sprite = oslLoadImageFile("sprite.png", OSL_IN_RAM, OSL_PF_5551);
bush = oslLoadImageFile("bush.png", OSL_IN_RAM, OSL_PF_5551);
While there are variables stored like
sprite->x = 3;
if ( (sprite->x + spritewidth > bush->x) && (sprite->x < bush->x + bushwidth) && (sprite->y + spriteheight > bush->y) && (sprite->y < bush->y + bushheight) )
{
bushcol = 1;
}
else
{
bushcol = 0;
}
So when i press a button
if (osl_keys->held.down)
{
if (bushcol == 1)
{
sprite->y = bush->y + 38;
}
else
{
sprite->y += 3;
}
}
if (osl_keys->held.up)
{
if (bushcol == 1)
{
sprite->y = bush->y - 23;
}
else
{
sprite->y -= 3;
}
}
if (osl_keys->held.right)
{
if (bushcol == 1)
{
sprite->x = bush->x - 28;
}
else
{
sprite->x += 3;
}
}
if (osl_keys->held.left)
{
if (bushcol == 1)
{
sprite->x = bush->x + 28;
}
else
{
sprite->x -= 3;
}
}
i was thinking of things like
sprite->y = bushheight - 24;
but it doesnt work
Any suggestions?
I'd suggest making a function solely for the purpose of bounding box colision detection.
It could look like
IsColiding(oslImage item1, oslImage item2)
{
/* Perform check */
}
in which you perform the check if there is a collision between image 1 and image 2.
As for the algorithm you're trying to use check out this wikipedia for example AABB bounding box
Especially this part:
In the case of an AABB, this tests
becomes a simple set of overlap tests
in terms of the unit axes. For an AABB
defined by M,N against one defined by
O,P they do not intersect if (Mx>Px)
or (Ox>Nx) or (My>Py) or (Oy>Ny) or
(Mz>Pz) or (Oz>Nz).
I think you have the basic idea. Just check your work. Here is a simple version which compiles:
#import <stdlib.h>
typedef struct {
// I'm going to say x, y, is in the center
int x;
int y;
int width;
int height;
} Rect;
Rect newRect(int x, int y, int w, int h) {
Rect r = {x, y, w, h};
return r;
}
int rectsCollide(Rect *r1, Rect *r2) {
if (r1->x + r1->width/2 < r2->x - r2->width/2) return 0;
if (r1->x - r1->width/2 > r2->x + r2->width/2) return 0;
if (r1->y + r1->height/2 < r2->y - r2->height/2) return 0;
if (r1->y - r1->height/2 > r2->y + r2->height/2) return 0;
return 1;
}
int main() {
Rect r1 = newRect(100,200,40,40);
Rect r2 = newRect(110,210,40,40);
Rect r3 = newRect(150,250,40,40);
if (rectsCollide(&r1, &r2))
printf("r1 collides with r2\n");
else
printf("r1 doesnt collide with r2\n");
if (rectsCollide(&r1, &r3))
printf("r1 collides with r3\n");
else
printf("r1 doesnt collide with r3\n");
}
First of all i suppose you mean
sprite->y = bush->**y** - 3;
second i dont know what platform you are using, but often times the y-coordinates are inverted. i.e., y=0 corresponds to top of the screen. In that case your comparisons might not work.
third collision checking can quickly become complicated when you add rotation and non-rectangular objects to it. You should consider using CGAL or some other computational geometry algorithms library, which can handle polygon intersection.
Related
I am currently trying to make a cellular automata. I am just getting the basics of direction, movement and rendering.
However, when running the compiler the cells don't move despite calling the Move function.
Here are the files,
Cell.cpp
#include "Cell.h"
#include "CellManager.h"
Cell::Cell()
{
Lifetime = 5 + rand() % 2 - 1;
}
Cell::~Cell()
{
}
void Cell::Move(int dir)
{
switch (dir)
{
default: y -= 2;
break;
case 0: y -= 2;
break;
case 1: x += 2;
break;
case 2: y += 2;
break;
case 3: x -= 2;
break;
}
if (x > 800)
{
x = 0;
} else if (x < 0)
{
x = 800;
}
if (y > 800)
{
y = 0;
}
else if (y < 0)
{
y = 800;
}
}
int Cell::ChangeDir(int dir)
{
dir = rand() % 3;
return dir;
}
void Cell::Draw(sf::RenderTarget& target)
{
sf::RectangleShape cell;
cell.setSize(sf::Vector2f(2.f,2.f));
cell.setOutlineColor(colour);
cell.setPosition(x, y);
target.draw(cell);
}
void Cell::SetUp(int X, int Y, sf::Color Colour, int dir)
{
x = X;
y = Y;
colour = Colour;
Dir = dir;
}
CellManager.cpp
#include "CellManager.h"
#include "Cell.h"
void CellManager::UpdateCells(vector<Cell> cells, sf::RenderTarget& target)
{
for (int i = 0; i < cells.size(); i++)
{
cells[i].ChangeDir(cells[i].Dir);
cells[i].Move(cells[i].Dir);
cells[i].Draw(target);
}
}
void CellManager::CreateInstance()//TODO
{
}
I do not understand where I am going wrong as the switch statement works but the cells just refuse to move. Any help would be appreciated :)
Your function, CellManager::UpdateCell's parameter vector<Cell> cells COPIES the vector of cells, then changes the copies. You probably want to pass by reference instead. This would look like: std::vector<Cell>& cells.
Other note:
ChangeDir does not change the member variable Dir. You could pass in by reference for that as well, or just not pass in anything at all and use Dir = rand() % 3;.
I am working on a 2D game using SDL. Recently I implemented various functions that put objects (and their rectangles) into motion, but encountered performance issues that are most likely caused by inefficient mapping of rectangle coordinates. Please see below:
2D coordinates of the rectangle are stored in an integer array whenever move() is called. For example, coordinate[0] is the first point on the x axis and coordinate[1] is the last point on the x axis. Coordinates [2] and [3] work for points on the y axis.
The map() function takes the coordinates of a rectangle and stores them in static std::map (Status class). Each x and y pair is either 0 or 1, depending on whether a rectangle is present or not. Player's coordinates are not mapped.
When the player moves, the bool function collide() checks whether the player's rectangle is adjacent to another recantgle in a particular direction. If there is no rectangle blocking the way, the player is allowed to move.
Everything works well, but it seems like all these for loops in the map() function are very CPU-heavy. When rectangles are being moved on the screen, the program lags horribly. How can I map rectangle coordinates more efficiently?
void move(int x, int y) {
dstRect.x = x;
dstRect.y = y;
coordinate[0] = dstRect.x;
coordinate[1] = dstRect.x + dstRect.w;
coordinate[2] = dstRect.y;
coordinate[3] = dstRect.y + dstRect.h;
}
void map() {
for (int x = coordinate[0]; x != coordinate[1]; x++) {
for (int y = coordinate[2]; y != coordinate[3]; y++) {
Status::map().insert(std::pair<std::vector<int>, int>({ x, y }, 1));
}
}
}
bool collide(DIRECTION direction) {
if (direction == UP || direction == DOWN) {
for (int x = texture.coordinate[0]; x != texture.coordinate[1]; x++) {
if (direction == UP) {
if (Status::map().find({ x, texture.coordinate[2] - 1 })->second == 1) { return true; }
}
if (direction == DOWN) {
if (Status::map().find({ x, texture.coordinate[3] + 1 })->second == 1) { return true; }
}
}
}
if (direction == RIGHT || direction == LEFT) {
for (int y = texture.coordinate[2]; y != texture.coordinate[3]; y++) {
if (direction == RIGHT) {
if (Status::map().find({ texture.coordinate[1] + 1, y })->second == 1) { return true; }
}
if (direction == LEFT) {
if (Status::map().find({ texture.coordinate[0] - 1, y })->second == 1) { return true; }
}
}
}
return false;
}
void moveRight() {
for (int i = 0; i < speed; i ++) {
if (!collide(RIGHT)) {
int x = texture.dstRect.x + 1;
int y = texture.dstRect.y;
texture.move(x, y);
}
}
}
Followed #FrançoisAndrieux advice and created multidimensional vector for storing the coordinates.
I don't work with tiles but cubes drawn with sf::Vertex. Each cubes have 6 sides with 4 points each.
So i just have to cubes[numCube].sides()[numSide].... to select a side.
I create cubes layer.cpp :
for(int J = 0; J < mapSize; J++)
{
for(int I = 0; I < mapSize; I++)
{
x = (J - I) * (cubeSize/2);
y = (J + I) * (cubeSize/4);
c = new cube(cubeSize, x, y, z, I, J);
cs.push_back(*c);
}
}
In cube.cpp i create sides, then, in sides.cpp, i calcul each points' coordinates like this :
switch(typeSide)
{
case 0://DOWN_SIDE
light = 1;
tmp_x = x + (size/2);
tmp_y = y + (size/2);
p0 = new point(tmp_x, tmp_y, tmp_z);
tmp_x = x + size;
tmp_y = y + (3 * (size/4));
p1 = new point(tmp_x, tmp_y, tmp_z);
tmp_x = x + (size/2);
tmp_y = y + size;
p2 = new point(tmp_x, tmp_y, tmp_z);
tmp_x = x;
tmp_y = y + (3 * (size/4));
p3 = new point(tmp_x, tmp_y, tmp_z);
break;
case 1://BACK_LEFT_SIDE
//ETC. ....
Point.cpp :
/*
* point.cpp
*
* Created on: 21 nov. 2015
* Author: user
*/
#include "point.h"
point::point(float tx, float ty, float tz)
{
coords* dummyVar = new coords(tx, ty, tz);
coordinates = dummyVar;
}
std::vector<float> point::position()//Use : myPoint.getPosition[0] //get the x
{
std::vector<float> dummyVar;
dummyVar.push_back(coordinates->getX());
dummyVar.push_back(coordinates->getY() - coordinates->getZ());
return dummyVar;
}
void point::move(float tx, float ty, float tz)
{
coordinates->setX(tx);
coordinates->setY(ty);
coordinates->setZ(tz);
}
My problem come from the function i use to detect click :
if (event.type == sf::Event::MouseMoved)
{
currentSelectedCube = maps[currentMapID].getCubeIDAt(event.mouseMove.x, event.mouseMove.y, offsetLeft, offsetTop, enableOffset);
}
The function(don't bother with the comments) :
I try to get a cube's entry in my cube vector without 'for loop'.
Why ? to use less CPU when i click.
int map::getCubeIDAt(float x, float y, int offsetLeft, int offsetTop, bool enableOffset)//WIP ! //USED FOR CLICK DETECTION ON CUBES
{
//----------------------------------------------------------------//
int unsigned entry = -1;
int I = 0;
int J = 0;
//----------------------------------------------------------------//
if(currentLayerId() > -1)//If there is any layers
{
//IF CHECK IN MAP BOUDING BOX + ROTATION TO GOT DIAMOND SHAPE AREA(LAYER + OFFSETS)----------------------------------
//{
if(!enableOffset)//With offsets disabled
{
I = (y * 2 - x) / cubeSize;
J = (y * 2 + x) / cubeSize;
}
else //With offsets enabled
{
I = (((y-offsetTop)+(currentLayerId()*(cubeSize/2))) * 2 - (x-offsetLeft)) / cubeSize;
J = (((y-offsetTop)+(currentLayerId()*(cubeSize/2))) * 2 + (x-offsetLeft)) / cubeSize;
}
entry = I + J * size;
if (entry < 0 || entry >= layers()[currentLayerId()].cubes().size())
{
entry = -1;
}
else//DEBUG - DISPLAYING VALUES FOR TEST
{
std::cout << "Entry n°" << entry << " - ";
std::cout << "[" << I << "; " << J << "]" << std::endl;
}
//}
//END IF CHECK IN MAP BOUDING BOX + ROTATION TO GOT DIAMOND SHAPE AREA(LAYER + OFFSETS)----------------------------------
}
return entry;
}
The I-J and entryNumber are OK. i mean, for example, for the cube 0, i have I = 0; J = 0; etc ... This is working.
I don't understand why the coordinate range is like the red part(not accurate at 100%, i'm not a paint genius ha ha) in this picture :
But i should get that(2nd picture - the red part is where i click) :
But after few checks, the I-J and the entry i got are corresponding. This is so weird.
EDIT2:
Offsets and layer number implemented.
Problem left: wrong coordinates range.
Just in case, this is the 'function' handling events :
void GRAPHICS_HANDLER::listenEvents()
{
while (window->pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window->close();
}
if(event.type == sf::Event::KeyPressed)
{
//DISPLAY/UNDISPLAY GRID -- DEBUG FUNCTION
if(event.key.code == sf::Keyboard::Escape)
{
if(grid)
grid = false;
else
grid = true;
}
//-----------------------------------------------------------------------------------DEBUG---------------------------------------------------------------//
if(event.key.code == sf::Keyboard::B)//ACTIVE BRUSHMODE -- NEED TO BLOCK IT WHEN ACCESS VIOLATION OF CUBES ARRAY(CRASH)
{
if(!brushMode)
{
brushMode = true;
std::cout << "Brush mode enabled" << std::endl;
}
else
{
brushMode = false;
std::cout << "Brush mode disabled" << std::endl;
}
}
if(event.key.code == sf::Keyboard::L)//ADD_LAYER
{
addLayer(getCurrentMapID());
}
if(event.key.code == sf::Keyboard::M)//DELETE_LAYER
{
deleteLayer(currentMapID, maps[currentMapID].currentLayerId());
}
if(event.key.code == sf::Keyboard::S)//ADD_LAYER
{
std::cout << "Select a texture: ";
std::cin >> currentSelectedTexture; std::cout << std::endl;
}
if(event.key.code == sf::Keyboard::Left)//Move in Layer
{
if(maps[currentMapID].currentLayerId() > 0)
{
maps[currentMapID].setCurrentLayerID(maps[currentMapID].currentLayerId()-1);
}
}
if(event.key.code == sf::Keyboard::Right)//Move in Layer
{
if(maps[currentMapID].currentLayerId() < maps[currentMapID].layers().size()-1)
{
maps[currentMapID].setCurrentLayerID(maps[currentMapID].currentLayerId()+1);
}
}
//-----------------------------------------------------------------------------------DEBUG---------------------------------------------------------------//
}
if (event.type == sf::Event::MouseMoved)
{
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
currentSelectedCube = maps[currentMapID].getCubeIDAt(event.mouseMove.x, event.mouseMove.y, offsetLeft, offsetTop, enableOffset);
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
}
if (event.type == sf::Event::MouseButtonPressed)
{
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
currentSelectedCube = maps[currentMapID].getCubeIDAt(event.mouseButton.x, event.mouseButton.y, offsetLeft, offsetTop, enableOffset);
//--------------------------------------------------------------------------CURSOR-----------------------------------------------------------------------//
if (event.mouseButton.button == sf::Mouse::Left)
{
//--------------------------------------------------------------------------CUBE CLICK DETECTION--------------------------------------------------//
if(maps.size() > 0 && maps[currentMapID].layers().size() > 0 && currentSelectedCube > -1)
{
cubeClicked = true;
}
}
if (event.mouseButton.button == sf::Mouse::Right)
{
if(maps.size() > 0 && maps[currentMapID].layers().size() > 0 && currentSelectedCube > -1)
{
maps[currentMapID].layers()[maps[currentMapID].currentLayerId()].cubes()[currentSelectedCube].setTexture(1);
}
}
//--------------------------------------------------------------------------CUBE CLICK DETECTION--------------------------------------------------//
}
}
}
EDIT3: I updated my code to allow me to draw only the down side of the cube, so i can do this(the grass) :
The coordinate range(the red isometric square shown before in the screenshots) change a little when i put flat square(green).
I don't know why, i prefer to precise it, just in case.
You need to store the "heigth" of each element from the tiles plane in order to distinguish which cube are you actually selecting (the closer to the observer):
Same screen coordinates, but different tiles.
It's not clear to me how you modeled your world, so I'll give you a partial algorithm to check what face of what cube is the one clicked. Please, adapt it to your actual code and to the classes you have written to make it work.
// I'll let you to add the offsets for the screen coordinates
I = (y * 2 - x) / cubeSize;
J = (y * 2 + x) / cubeSize;
// find out if it is a left or right triangle
if ( x < (J - I) * (cubeSize/2) ) {
// left triangle
for ( k = max_n_layer; k > -1; --k ) {
// you create the cubes nesting the I loop in the J loop, so to get the index of a cube,
// assuming that you have created all the cubes (even the invisible ones, like it seems from your code)
index = (J+1+k)*mapsize + I+1+k;
// I don't really get how you define the existence or not of a face, but I guess something like this:
if ( index < map.layer[k].cubes.size()
&& map.layer[k].cubes[index].sides[top_side] != 0 ) {
// the face selected is the top side of cube[index] of layer k
// you have to return index and k to select the right face, or simply a pointer to that face
// if this makes any sense with how you have designed your model
return &map.layer[k].cubes[index].sides[top_side];
}
// now check for the side
index = (J+k)*mapsize + I+1+k;
if ( index < map.layer[k].cubes.size()
&& map.layer[k].cubes[index].sides[right_side] != 0 ) {
return &map.layer[k].cubes[index].sides[right_side];
}
index = (J+k)*mapsize + I+k;
if ( index < map.layer[k].cubes.size()
&& map.layer[k].cubes[index].sides[left_side] != 0 ) {
return &map.layer[k].cubes[index].sides[left_side];
}
}
} else {
// right triangle
for ( k = max_n_layer; k > -1; --k ) {
index = (J+1+k)*mapsize + I+1+k;
if ( index < map.layer[k].cubes.size()
&& map.layer[k].cubes[index].sides[top_side] != 0 ) {
return &map.layer[k].cubes[index].sides[top_side];
}
index = (J+1+k)*mapsize + I+k;
if ( index < map.layer[k].cubes.size()
&& map.layer[k].cubes[index].sides[left_side] != 0 ) {
return &map.layer[k].cubes[index].sides[left_side];
}
index = (J+k)*mapsize + I+k;
if ( index < map.layer[k].cubes.size()
&& map.layer[k].cubes[index].sides[right_side] != 0 ) {
return &map.layer[k].cubes[index].sides[right_side];
}
}
}
// well, no match found. As I said is up to you to decide how to do in this case
return nullptr;
Edit
I suggest you to try another way.
Consider the screen as divided not by quadrangular tiles but by the triangles you already depicted. Every 2D tile of your model will be formed by two of those triangles and so all the sides of the cubes you want to draw. For every cube don't draw nor even create the back sides, those will never be drawn.
You can try to implement a sort of specialized z-buffer algorithm by storing for each one of the triangles you have to draw on the screen the index of the side which is closer to the observer.
The coordinates of the vertex of all the triangles are calculated (once) with the code you already have.
(I,J) //For every node (I,J) you have a left and a right triangle
. * .
(I+1,J) * . | . * (I,J+1)
*
(I+1,J+1)
You are creating your cubes layer by layer, I guess, each layer having a different heigth over the base plane. Create every side of the cube using the coordinates calculated earlier. For every face (only the 3 pointing to the observer) consider each one of its 2 triangles. You can easily determine if it is visible or not if you proceed in order, then you only have to update the ID stored in the corresponding triangle.
Once finished this fase, you'll have to draw each triangle once as you already have dropped the hidden ones.
To determine the inverse transformation from screen coordinates to cell indexes, you only have to calculate which triangle is hitted and then look up which ID correspond to that. So transform back x,y to I,J (you already have those equations) and choose the left triangle if x < (J-I)/cubesize the right one otherwise.
My computer graphics homework is to implement OpenGL algorithms using only the ability to draw points.
So obviously I need to get drawLine() to work before I can draw anything else. drawLine() has to be done using integers only. No floating point.
This is what I was taught. Basically, lines can be broken up into 4 different categories, positive steep, positive shallow, negative steep and negative shallow. This is the picture I am supposed to draw:
and this is the picture my program is drawing:
The colors are done for us. We are given vertices and we need to use Bresenham's Line algorithm to draw the lines based on the start and end points.
This is what I have so far:
int dx = end.x - start.x;
int dy = end.y - start.y;
//initialize varibales
int d;
int dL;
int dU;
if (dy > 0){
if (dy > dx){
//+steep
d = dy - 2*dx;
dL = -2*dx;
dU = 2*dy - 2*dx;
for (int x = start.x, y = start.y; y <= end.y; y++){
Vertex v(x,y);
drawPoint(v);
if (d >= 1){
d += dL;
}else{
x++;
d += dU;
}
}
} else {
//+shallow
d = 2*dy - dx;
dL = 2*dy;
dU = 2*dy - 2*dx;
for (int x = start.x, y = start.y; x <= end.x; x++) {
Vertex v(x,y);
drawPoint(v);
// if choosing L, next y will stay the same, we only need
// to update d by dL
if (d <= 0) {
d += dL;
// otherwise choose U, y moves up 1
} else {
y++;
d += dU;
}
}
}
} else {
if (-dy > dx){
cout << "-steep\n";
//-steep
d = dy - 2*dx;
//south
dL = 2*dx;
//southeast
dU = 2*dy - 2*dx;
for (int x = start.x, y = start.y; y >= end.y; --y){
Vertex v(x,y);
drawPoint(v);
//if choosing L, next x will stay the same, we only need
//to update d
if (d >= 1){
d -= dL;
} else {
x++;
d -= dU;
}
}
} else {
cout << "-shallow\n";
//-shallow
d = 2*dy - dx;
dL = 2*dy;
dU = 2*dy - 2*dx;
for (int x = start.x, y = start.y; x <= end.x; x++){
Vertex v(x,y);
drawPoint(v);
if (d >= 0){
d += dL;
} else {
--y;
d -= dU;
}
}
}
}
I know my error is going to be something silly, but I honestly cannot figure out what I am doing wrong. Why are some of the lines drawn incorrectly as shown above?
/*BRESENHAAM ALGORITHM FOR LINE DRAWING*/
#include<iostream.h>
#include<graphics.h>
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
#include<dos.h>
void bhm_line(int,int,int,int,int);
void main()
{
int ghdriver=DETECT,ghmode,errorcode,x1,x2,y1,y2;
initgraph(&ghdriver,&ghmode,"..\\bgi");
errorcode = graphresult();
if(errorcode !=grOk)
{
cout<<"Graphics error:%s\n"<<grapherrormsg(errorcode);
cout<<"Press any key to halt:";
getch();
exit(1);
}
clrscr();
cout<<"Enter the coordinates (x1,y1): ";
cin>>x1>>y1;
cout<<"Enter the coordinates (x2,y2): ";
cin>>x2>>y2;
bhm_line(x1,y1,x2,y2,1);
getch();
}
void bhm_line(int x1,int y1,int x2,int y2,int c)
{
int x,y,dx,dy,dx1,dy1,px,py,xe,ye,i;
dx=x2-x1;
dy=y2-y1;
dx1=fabs(dx);
dy1=fabs(dy);
px=2*dy1-dx1;
py=2*dx1-dy1;
if(dy1<=dx1)
{
if(dx>=0)
{
x=x1;
y=y1;
xe=x2;
}
else
{
x=x2;
y=y2;
xe=x1;
}
putpixel(x,y,c);
for(i=0;x<xe;i++)
{
x=x+1;
if(px<0)
{
px=px+2*dy1;
}
else
{
if((dx<0 && dy<0) || (dx>0 && dy>0))
{
y=y+1;
}
else
{
y=y-1;
}
px=px+2*(dy1-dx1);
}
delay(0);
putpixel(x,y,c);
}
}
else
{
if(dy>=0)
{
x=x1;
y=y1;
ye=y2;
}
else
{
x=x2;
y=y2;
ye=y1;
}
putpixel(x,y,c);
for(i=0;y<ye;i++)
{
y=y+1;
if(py<=0)
{
py=py+2*dx1;
}
else
{
if((dx<0 && dy<0) || (dx>0 && dy>0))
{
x=x+1;
}
else
{
x=x-1;
}
py=py+2*(dx1-dy1);
}
delay(0);
putpixel(x,y,c);
}
}
}
I implemented the original Bresenham's algorithm in C++ and tried to optimize as much as I could (especially regarding removing the IF from the interior loop).
It draws in a linear buffer instead of a surface, and for this matter, this implementation was almost as fast as EFLA (Extremely Fast Line Algorithm) (maybe 5% slower).
#include <vector>
#include <math.h>
using namespace std;
vector<unsigned char> buffer;
int imageSide = 2048; // the width of the surface
struct Point2Di
{
int x;
int y;
Point2Di(const int &x, const int &y): x(x), y(y){}
Point2Di(){}
};
void drawLine(const Point2Di &p0, const Point2Di &p1)
{
int dx = p1.x - p0.x;
int dy = p1.y - p0.y;
int dLong = abs(dx);
int dShort = abs(dy);
int offsetLong = dx > 0 ? 1 : -1;
int offsetShort = dy > 0 ? imageSide : -imageSide;
if(dLong < dShort)
{
swap(dShort, dLong);
swap(offsetShort, offsetLong);
}
int error = 2 * dShort - dLong;
int index = p0.y*imageSide + p0.x;
const int offset[] = {offsetLong, offsetLong + offsetShort};
const int abs_d[] = {2*dShort, 2*(dShort - dLong)};
for(int i = 0; i <= dLong; ++i)
{
buffer[index] = 255; // or a call to your painting method
const int errorIsTooBig = error >= 0;
index += offset[errorIsTooBig];
error += abs_d[errorIsTooBig];
}
}
The EFLA implementation that I am using is:
void drawLine(Point2Di p0, Point2Di p1)
{
bool yLonger=false;
int shortLen=p1.y-p0.y;
int longLen=p1.x-p0.x;
if (abs(shortLen)>abs(longLen)) {
swap(shortLen, longLen);
yLonger=true;
}
int decInc = longLen==0 ? decInc=0 : ((shortLen << 16) / longLen);
if (yLonger) {
p0.y*=imageSide;
p1.y*=imageSide;
if (longLen>0)
for (int j=0x8000+(p0.x<<16);p0.y<=p1.y;p0.y+=imageSide, j+=decInc)
buffer[p0.y + (j >> 16)] = 255; // or a call to your painting method
else
for (int j=0x8000+(p0.x<<16);p0.y>=p1.y;p0.y-=imageSide, j-=decInc)
buffer[p0.y + (j >> 16)] = 255; // or a call to your painting method
}
else
{
if (longLen>0)
for (int j=0x8000+(p0.y<<16);p0.x<=p1.x;++p0.x, j+=decInc)
buffer[(j >> 16) * imageSide + p0.x] = 255; // or a call to your painting method
else
for (int j=0x8000+(p0.y<<16);p0.x>=p1.x;--p0.x, j-=decInc)
buffer[(j >> 16) * imageSide + p0.x] = 255; // or a call to your painting method
}
}
In case anyone was wondering what the problem was, I still don't know what it was. What I ended up doing was re-factored my code so that the -shallow and -steep used the same algorithm as +shallow and +steep, respectively. After adjusting the x,y coordinates (negating the x or y coordinate), when I went to plot them I negated my original negation so that it plotted in the right spot.
Right now when i start my game am making in C++ i walk left right up or down.. But the character just slides doesn't look like he's walking.. And i have all the pictures already loaded into my game and they are working.. But i don't know how i would solve it.. Thing i can't figure out is how to make the picture change when u hold the button..
This is in allegro by the way..
Here is my code for the drawing player:
void Player::Draw(BITMAP *Buffer){
draw_sprite(Buffer, Oskar[picNumber], x, y);
}
Oskar[] is the name of the Array with all the pictures..
Here is what changes the picture for the character when u press the buttons:
void Player::Controls(){
if(key[KEY_RIGHT]){
velocityX = speed;
picNumber = 6;
}
else if(key [KEY_LEFT]){
velocityX = -speed;
picNumber = 9;
}
else{
velocityX = 0;
}
if(key [KEY_UP]){
velocityY = -speed;
picNumber = 3;
}
else if(key [KEY_DOWN]){
velocityY = speed;
picNumber = 0;
}
else{
velocityY = 0;
}
x += velocityX;
y += velocityY;
}
Its all about the variable i created picNumber.. All pictures i have is in an Array and the picNumber is represents what picture to be drawn..
Would be nice to get some help on this.. I've been thinking all day about this..
EDIT
#include "Player.h"
#include "Global.h"
#include <allegro.h>
Player::Player(){
}
Player::~Player(){
}
void Player::Init(){
x = 10;
y = 10;
velocityX = 0;
velocityY = 0;
speed = 1;
picNumber = x % MAXPICS;
OskarFront[0] = load_bitmap("Character\\OskarFront.bmp", NULL);
OskarFront[1] = load_bitmap("Character\\OskarStanding.bmp", NULL);
OskarFront[2] = load_bitmap("Character\\OskarFront2.bmp", NULL);
OskarBack[0] = load_bitmap("Character\\OskarBack.bmp", NULL);
OskarBack[1] = load_bitmap("Character\\OskarStandingBack.bmp", NULL);
OskarBack[2] = load_bitmap("Character\\OskarBack2.bmp", NULL);
OskarRight[0] = load_bitmap("Character\\Oskar1.bmp", NULL);
OskarRight[1] = load_bitmap("Character\\Oskar.bmp", NULL);
OskarRight[2] = load_bitmap("Character\\Oskar2.bmp", NULL);
OskarLeft[0] = load_bitmap("Character\\OskarLeft.bmp", NULL);
OskarLeft[1] = load_bitmap("Character\\OskarLeftStand.bmp", NULL);
OskarLeft[2] = load_bitmap("Character\\OskarLeft2.bmp", NULL);
}
void Player::Update(){
Player::Controls();
}
void Player::Draw(BITMAP *Buffer){
if(walkingRight == true){
draw_sprite(Buffer, OskarRight[picNumber], x, y);
}
else if(walkingLeft == true){
draw_sprite(Buffer, OskarLeft[picNumber], x, y);
}
else if(walkingFront == true){
draw_sprite(Buffer, OskarFront[picNumber], x, y);
}
else if(walkingBack == true){
draw_sprite(Buffer, OskarBack[picNumber], x, y);
}
else{
draw_sprite(Buffer, OskarFront[1], x, y);
}
}
void Player::Controls(){
if(key[KEY_RIGHT]){
velocityX = speed;
walkingRight = true;
}
else if(key [KEY_LEFT]){
velocityX = -speed;
walkingLeft = true;
}
else{
walkingRight = false;
walkingLeft = false;
velocityX = 0;
}
if(key [KEY_UP]){
velocityY = -speed;
walkingFront = true;
}
else if(key [KEY_DOWN]){
velocityY = speed;
walkingBack = true;
}
else{
velocityY = 0;
walkingFront = false;
walkingBack = false;
}
x += velocityX;
y += velocityY;
}
here is now the new full code i typed after getting help here.. Its now not working when am walking up its showing the front picture and am walking down the up picture is showing.. But left and right works.. Also its not changing picture like animation..
You only have one index for each direction of movement!
Assuming you have drawn about 3 (left leg forward, overlap, right leg forward) you would need to increment the index with time or movement as the march progresses.
Interesting how far Allegro has progressed- a decade ago it was mainly DOS graphics (I used it with DJGPP) and had a note from the author living in a London flat. How austere I thought, only now do I see how far off of London prices we provincials are.
In answer to your comment, try something like:
#define MAXPICS 3
BITMAP *OskarWalksLeft[MAXPICS];
BITMAP *OskarWalksRight[MAXPICS];
picNumber = x % MAXPICS;
if (facingLeft) {
draw_sprite(Buffer, OskarWalksLeft[picNumber], x, y);
} else //facingRight
draw_sprite(Buffer, OskarWalksRight[picNumber], x, y);
or if you don't want a new frame per pixel but every 3 or so try replacing
picNumber = x % MAXPICS;
with
picNumber = (x / 3) % MAXPICS;
I've tried tidying up your code a little and come up with this, I don't have allegro so couldn't test it:
#include "Player.h"
#include "Global.h"
#include <allegro.h>
Player::Player(){}
Player::~Player(){}
enum playerDir {left, right, front, back, stationary} direction;
BITMAP *Oskar[stationary][3];
void Player::Init(){
x = 10;
y = 10;
velocityX = 0;
velocityY = 0;
speed = 1;
picNumber = x % MAXPICS;
char filename[256];
const char *dirStrs[] = {"left","right","front","back"};
for (playerDir i = left; i < stationary; (int)i = (int)i + 1)
for (j = 0; j < 3; ++j) {
strcpy(filename, "Character\\Oskar");
strcat(filename, dirStrs[i]);
strcat(filename, itoa(j));
strcat(filename, ".bmp");
Oskar[i][j] = load_bitmap(filename, NULL);
}
}
void Player::Update(){Player::Controls();}
void Player::Draw(BITMAP *Buffer){
switch (direction) {
case left :
draw_sprite(Buffer, OskarLeft[picNumber], x, y);
break;
case right :
draw_sprite(Buffer, OskarRight[picNumber], x, y);
break;
case front :
draw_sprite(Buffer, OskarFront[picNumber], x, y);
break;
case back :
draw_sprite(Buffer, OskarBack[picNumber], x, y);
break;
default:
draw_sprite(Buffer, OskarFront[1], x, y);
}
}
void Player::Controls(){
direction = stationary;
if(key[KEY_RIGHT]){
velocityX = speed;
direction = right;
} else if(key [KEY_LEFT]){
velocityX = -speed;
direction = left;
} else velocityX = 0;
if(key [KEY_UP]){
velocityY = -speed;
direction = front;
} else if(key [KEY_DOWN]){
velocityY = speed;
direction = back;
} else velocityY = 0;
x += velocityX;
y += velocityY;
}
You don't seem to be doing any animation. It seems you are just setting a specific frame based on the direction key the user is pressing. You need a function that does something like this:
void Player::updateFrame(float time_delta)
{
time_to_next_frame -= time_delta;
while (time_to_next_frame <= 0.0f)
{
nextFrame();
time_to_next_frame += time_per_frame;
}
}
Call it every frame, passing it the amount of time that has elapsed since the last frame.
In response to your comments on the other 2 answers, you seem to be unsure of how to not go over a specific value.
To do this (for instance you want something in the range [0, 2] + c, where c is some constant determining the start of your index. So for instance, you could do the following:
void Player::nextFrame( int startIndex /* Changes based on your direction */) {
static unsigned short iIndex = 0;
// Increment and map value to the range [0, 2]
++iIndex;
iIndex %= 3;
// Note: this assumes that picNumber is a member function, else you
// will have to pass it by reference to this function.
picNumber = iIndex + startIndex;
}
This should do what you want (i.e. depending on the direction you change the start index to suit, and then it increments, mapping the value to the range [0, 2].
Hope this helps!