I have an if statement that checks whether the group(consists of a rectangle and a text) I'm dragging is over other group(I check only "y" coordinate because "x" axis is locked). If so, change the color of the stationary group(rectangle) to "red". Else make all rectangles blue. Every group is in groupfield called "groups" which I'm iterating and adding events like this 'dragmove' + others. For some reason both blocks of code executes, idk why. Height of all rectangles is set by text height which is same everywhere(and is accessed by groups[].children[1].height(). Groups[i] is the moving(dragging) group and groups[j] are stationary. In my other event("dragstart") I change the "name" of that group to "true" others are false by default so when i iterate in my if doesn't triggers the dragging group. The code works partially, colors change how they should, but "else" is running even if I drag rectangle over other rectangle which is unwanted and needs fixing. Here's my code:
groups[i].on('dragmove', () => {
//lock top stage&bottom
if(groups[i].y() <= 0){
groups[i].y(0);
}
else if (groups[i].y() + groups[i].children[1].height() >= stage.height()){
groups[i].y(stage.height() - groups[i].children[1].height());
}
//end lock top stage&bottom
for (let j = 0; j < groups.length; j++) {
if(groups[i].y() + groups[i].children[1].height() >= groups[j].y() &&
groups[i].y() <= groups[j].y() + groups[j].children[1].height() &&
groups[j].name !== "true"){
if (groups[j].children[0].fill() === "red"){
continue;
}
console.log("if dragmove");
groups[j].children[0].fill("red");
}
else {
groups[j].children[0].fill("#aaf");
}
}
})
Related
My grid movement is too fast. This problem is due to speed, but I can't change it. Because then the grid doesn't work.
if (Game::event.type == SDL_KEYDOWN) {
if (Game::event.key.keysym.sym == SDLK_w || Game::event.key.keysym.sym == SDLK_UP) {
transform->velocity.y = -1;
}
else if (Game::event.key.keysym.sym == SDLK_s || Game::event.key.keysym.sym == SDLK_DOWN) {
transform->velocity.y = 1;
}
else if (Game::event.key.keysym.sym == SDLK_d || Game::event.key.keysym.sym == SDLK_RIGHT) {
transform->velocity.x = 1;
}
else if (Game::event.key.keysym.sym == SDLK_a || Game::event.key.keysym.sym == SDLK_LEFT) {
transform->velocity.x = -1;
}
} else if (Game::event.type == SDL_KEYUP) {
transform->velocity.x = 0;
transform->velocity.y = 0;
}
And update for player position:
void update() override {
position.x += round(velocity.x * 32);
position.y += round(velocity.y * 32);
}
Problem is in update player position, but if there weren't *32 player gets out from grid. (32 is grid size)
Any idea how to solve it?
And yes, I use SDL_Delay.
If you want only one step per press then in your update() function you need to zero your velocity vector after you're done using it, that will make it step only once, but you also have to check event.key.repeat and only accept events when it's 0 to avoid repeating the event ~30 times/second as long as the key is held down. If you want the stepping to repeat after a given delay then you'll need to store the return value (a uint32_t or Uint32 if you prefer) of SDL_GetTicks(); taken the moment a SDL_KEYUP event was received (with repeats ignored) then check if enough milliseconds have passed to repeat the stepping then add the time threshold to the stored time value to properly wait for the next repeating.
Also you're zeroing your velocity vector every time any key is released, which in some cases you don't want. It might be better to add to the vector for each SDL_KEYDOWN event and subtract to under for each SDL_KEYUP, for matching keys of course. Also if you're gonna use WASD you should use scancodes instead of keysym, or your control scheme will be quite awkward on non-QWERTY layouts.
Also there's probably no need for SDL_Delay(), just use Vsync which is set when you initialise your renderer with something like renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);.
Hi I am building a basic game in wich I have to detect collisions between an array of sprites. For now the sprites just move forward and change direction randomly or when reached a screen edge. The game is made in C++ with my college graphich library.
I implemented a basic algorithm for detecting collisions into this function:
bool DetectarColisionTanques(strTanque tanque1, strTanque tanque2) {
if(
(tanque1.px + tanque1.width >= tanque2.px) &&
(tanque1.px <= tanque2.px + tanque2.width) &&
(tanque1.py + tanque1.height >= tanque2.py) &&
(tanque1.py <= tanque2.py + tanque2.height)
) {
return true;
} else {
return false;
}
}
Sorry that code is in spanish, tanque1 and tanque2 are 2 structs containing each sprite coordinates wich I want to see if collide, px and py are the sprite origin at the upper left edge of the image.
But when I try to run the function i don't get the expected feedback:
void MoverEnemigos(){
for (int i=0;i<nEnemigos;i++) {
switch (enemigos[i].dir) {
case 0: enemigos[i].py-=enemigos[i].vel;break;
case 1: enemigos[i].px+=enemigos[i].vel;break;
case 2: enemigos[i].py+=enemigos[i].vel;break;
case 3: enemigos[i].px-=enemigos[i].vel;break;
for (int k= i+1; k<nEnemigos; k++) {
if(DetectarColisionTanques(enemigos[i],enemigos[k])) {
printf("Collision detected");
printf("\n");
}
}
}
enemigos[i].width= esat::SpriteWidth(spriteTanque[enemigos[i].dir]);
enemigos[i].height= esat::SpriteHeight(spriteTanque[enemigos[i].dir]);
}
}
In this function called MoverEnemigos() I move all the sprites px and py depending on their direction and then I try to call the function DetectarColisionTanques() to see if I get the printf at the same time I see in my graphic window that the sprites are obviously colliding. But I never ever get the printf.
PD: All the functions are running in a while at 60fps.
So I am trying to make a game using SFML and visual c++. Currently I have different sprites without borders on certain sides for every combination of tiles that could be bordering each other, then used nested if statements to check which sprite to use. However, this slows down my game to the point where I can't even play it. Is there a better way to do this?
Here is my code:
//Render tiles
for (int i = 0; i < blockX.size(); i++) {
block.setPosition(float(blockX[i]), float(blockY[i]));
if (blockType[i] == "metal") {
//if block is metal tile
block.setTexture(metalBlockSingle);
if (doesBlockExist(blockX[i] + 20, blockY[i])) {
//if block is to right
if (doesBlockExist(blockX[i], blockY[i] + 20)) {
//if block is below
if (doesBlockExist(blockX[i] - 20, blockY[i])) {
//if block is to left
if (doesBlockExist(blockX[i], blockY[i] - 20)) {
//if block is above
block.setTexture(metalBlockCenter);
}
else {
block.setTexture(metalBlockBottomRightLeft);
}
}
else if (doesBlockExist(blockX[i], blockY[i] - 20)) {
//if block is above
block.setTexture(metalBlockTopBottomRight);
}
else {
//if block is neither above nor to left
block.setTexture(metalBlockBottomRight);
}
}
else if (doesBlockExist(blockX[i] - 20, blockY[i])) {
//if block is to left
if (doesBlockExist(blockX[i], blockY[i] - 20)) {
//if block is above
block.setTexture(metalBlockTopRightLeft);
}
else {
block.setTexture(metalBlockRightLeft);
}
}
else if (doesBlockExist(blockX[i], blockY[i] - 20)) {
//if block is above
block.setTexture(metalBlockTopRight);
}
else {
//if block is only right
block.setTexture(metalBlockRight);
}
}
else if (doesBlockExist(blockX[i], blockY[i] + 20)) {
//if block is below
if (doesBlockExist(blockX[i] - 20, blockY[i])) {
//if block is to left
if (doesBlockExist(blockX[i], blockY[i] - 20)) {
//if block is above
block.setTexture(metalBlockTopBottomLeft);
}
else {
//if block is not above
block.setTexture(metalBlockBottomLeft);
}
}
else {
//if block is not to left
if (doesBlockExist(blockX[i], blockY[i] - 20)) {
//if block is above
block.setTexture(metalBlockTopBottom);
}
else {
//if metal block is not above
block.setTexture(metalBlockBottom);
}
}
}
else if (doesBlockExist(blockX[i] - 20, blockY[i])) {
//if block is to left
if (doesBlockExist(blockX[i], blockY[i] - 20)) {
//if block is above
block.setTexture(metalBlockTopLeft);
}
else {
block.setTexture(metalBlockLeft);
}
}
else if (doesBlockExist(blockX[i], blockY[i] - 20)) {
//if block is above
block.setTexture(metalBlockTop);
}
}
window.draw(block);
}
EDIT: Here is the code for the "doesBlockExist" function:
bool doesBlockExist(int x, int y) {
bool returnVal = false;
int findX = int(round(x / 20) * 20);
int findY = int(round(y / 20) * 20);
for (int i = 0; i < blockX.size(); i++) {
if (blockX[i] == findX && blockY[i] == findY) {
returnVal = true;
}
}
return(returnVal);
return(true);
}
Whenever you have nested if-statements (high cyclomatic complexity) like this, you should immediately stop and think about other designs. Large nested if-statements are not only prone to contain mistakes, but are impossible to really understand at a glance.
A better design can start with splitting things into multiple function, each with their own single responsibility. Other steps involve thinking about the logic hard enough to find ways that eliminate conditions, i.e. when situation X holds up, Y follows and there's no need for further checking. And then there are also situations where instead of using if-statements you can construct an object that will have the right settings after construction.
As for optimizations on your code:
Use a sprite sheet and don't use x amount of textures. setTextureRect is basically a free function, while switching textures all the time, can lead to performance issues.
If you keep the implementation of doesBlockExist, at least make sure to return right when you find the block and don't keep iterating. This means, inside the if-body just write return true and after the loop write return false and delete the variable returnVal.
However the doesBlockExist function seems badly implemented. What exactly does it check for? Do you just check the surrounding tiles? If so, then you can easily just check the 8 surrounding tiles and don't need to iterate over all the tiles.
Since you call doesBlockExist quite often, you'll have to think about, whether it wouldn't be possible to just call it once and report on all the surrounding tiles at once.
int(round(x / 20) * 20); since x is already int x / 20 will be an integer division anyways, so there's no reason to round.
Alternatively you could just use a tile editor like Tiled and place the tiles accordingly when designing the level and then just load the tile map.
As for the question in the title, I have no idea what you mean with borders.
I am currently experiencing some heavy slowdowns with my game. I have narrowed it down to something related with texture animations.
In my game there are characters that walk in 1 of 4 possible directions, they will walk up to a point, then change direction and continue walking (sort of like a tower defense game).
First i am loading the sprite frame cache like this
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("characters.plist");
This code is only run once during the life time of my application.
When the characters get loaded to the screen their animation is being set using the following code:
int direction = 0;
int number = 0;
if (this->to_x < 0) // Left
{
direction = 1;
number = 1;
}
else if(this->to_x > 0) // Right
{
direction = 2;
number = 1;
}
if (this->to_y < 0) // Down
{
direction = 0;
number = 0;
}
else if(this->to_y > 0) // Up
{
direction = 3;
number = 2;
}
int s = 0; //skin
// Set the animation
Animation *animation = Animation::create();
for (int i = 0; i < INT16_MAX; i++)
{
string frame_sprite_name = StringUtils::format("%s_%d_%d_%d.png",parameters[name].image_name.c_str(),s,number,i);
auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(frame_sprite_name);
if (frame) {
animation->addSpriteFrame(frame);
} else {
break;
}
}
// Invert the sprite when they go right
if (direction == 2) {
setFlippedX(true);
}else{
setFlippedX(false);
}
// Set the pace of the animation based on the type
if (name=="runner") {
animation->setDelayPerUnit(0.15f);
} else{
animation->setDelayPerUnit(0.3f);
}
Animate *animate = Animate::create(animation);
this->stopAllActions();
this->runAction(RepeatForever::create(animate));
What this code does is:
Check the direction
Get the sprite frame from the cache based on the direction
Run the action with repeat forever.
However this code is ran every time they change direction to set the new animation of the active characters. Also, at one time I can have around 40-50 of these characters going around.
I've noticed that after a few minutes in the game the slowdown starts to happen as soon as a new "character" is created, (since they are created in rapid succession in waves). And the slowdown also happens when the characters change in direction. So this makes me believe I am using the textures wrong.
If anyone knows how to fix this please let me know.
PD: I was thinking about the possibility of pre-loading all the animations and then just having each of the sprites that represent the characters run the corresponding animation.
You should definitely cache the animation in the AnimationCache with addAnimation and getAnimation methods.
Alright so I have this tic tac toe game I'm making with SDL and C++. I'm trying to implement AI into the game. I don't have a problem setting up the AI, but I have a problem making it where you can take turns. My problem is that when I make my move, I can just move as many times as I want before the AI moves. I want it so I can make my move, and I can't make my move again until the AI makes a move. No matter what I do it seems the turn taking doesn't work properly.
This is the class header
class Buttons
{
private:
SDL_Rect squares[8];
public:
Buttons();
void handle_input();
void load_Squares(SDL_Rect sqRects, SDL_Texture* squarTexs);
void show_Squares();
void AI_move();
int grid[9];
bool moveMade = true;
};
Here I check for mouse input, and depending on the location during the left button press, it sets the according grid value to equal 1, meaning it becomes displayed as a circle on the screen. I also make sure that the AI has made a move before it allows me to click.
void Buttons::handle_input()
{
double mouseX = 0, mouseY = 0;
if((event.type == SDL_MOUSEBUTTONDOWN))
{
//If left mouse button was clicked and AI has made a move
if(event.button.button == SDL_BUTTON_LEFT && moveMade == true)
{
//Get mouse location
mouseX = event.button.x;
mouseY = event.button.y;
//If mouse location is in particular square, set according grid value to 1
if((mouseX >= 0) && (mouseX < SCREEN_WIDTH / 3) && (mouseY >= 0) && (mouseY < SCREEN_HEIGHT / 3) && (grid[0] == 0))
{
grid[0] = 1;
moveMade = false;
}
//Basically does this for all other 9 grids
Here is my AI function, where I check to make sure the moveMade variable = false. Every time I make a move in the input function above, it sets moveMade to false, which means it should access this function, and only until it finishes this AI_move function should I be able to make a move again, because moveMade is set back equal to true.
void Buttons::AI_move()
{
if(moveMade == false)
{
AI_block(&moveMade);
AI_complete(&moveMade);
AI_rand(&moveMade);
moveMade = true;
}
}
Last is my show function, where I show a Circle(player) if the grid array value = 1, and I show the X(AI) if the grid value = 2.
void Buttons::show_Squares()
{
switch(grid[0])
{
case 1:
load_Squares(squares[0], circleTexture); break;
case 2:
load_Squares(squares[0], xTexture); break;
}
switch(grid[1])
{
//Does this all the way to grid[8]
}
Alright so my problem doesn't have to do with the AI dealing accordingly, as I haven't even set up my defense and offense functions. My problem is that I can make another move before the AI moves. Sorry if this is way too long, but if I could get any feedback on this that would be great.
Have you tried putting breakpoints at various points such as if(event.button.button == SDL_BUTTON_LEFT && moveMade == true) and then following the program through the see if moveMade ever actually gets changed to false?
You should also look at changing show_Squares() into a loop as there is a lot of repeated code using incremented indexes. Something like this:
void Buttons::show_Squares()
{
size_t array_size = sizeof(squares) / sizeof(int); //gets the number of elements in the array
for(size_t i = 0; i < array_size; i++)
{
switch(grid[i])
{
case 1:
load_Squares(squares[i], circleTexture); break;
case 2:
load_Squares(squares[i], xTexture); break;
}
}
}