SFML Networking - Error Sending Packet through UDP protocol - c++

I'm pretty new to SFML networking and have been working on a pong clone for quite some time. I've written two programs: a client and a server. My problem is that the two don't seem to properly send/receive data between each other. I've checked and my ports properly bind and my packet isn't larger than the max datagram size. However, when I check the status of socket.send(...) against Socket::Done it finds that the data did not send. Is there something I'm doing wrong?
Client:
#include <SFML/Graphics.hpp>
#include <SFML/Network.hpp>
#include <ctime>
#include <iostream>
#include <sstream>
using namespace sf;
using namespace std;
template <typename T>
string NumberToString ( T Number )
{
stringstream ss;
ss << Number;
return ss.str();
}
int main()
{
cout << "Enter an IP address: ";
string address;
cin >> address;
IpAddress other = address;
//window
RenderWindow window(VideoMode(800, 600), "Pong");
//player 1
RectangleShape rect(Vector2f(40, 130));
rect.setPosition(40, 50);
rect.setFillColor(Color::Blue);
rect.setOutlineThickness(4);
rect.setOutlineColor(Color::Black);
//player 2
RectangleShape player2(Vector2f(40, 130));
player2.setPosition(520,50);
player2.setFillColor(Color(255, 0, 208));
player2.setOutlineThickness(4);
player2.setOutlineColor(Color::Black);
//ball
CircleShape ball(50, 50);
ball.setOrigin(ball.getRadius(), ball.getRadius());
ball.setPosition(400, 300);
ball.setFillColor(Color(255, 255, 0));
ball.setOutlineThickness(4);
ball.setOutlineColor(Color::Black);
//score
Uint16 score1;
Uint16 score2;
//socket
short unsigned int port = 54000;
UdpSocket socket;
if (socket.bind(port) != Socket::Done)
{
cout << "Error\n";
}
socket.setBlocking(false);
//text
Font font;
font.loadFromFile("SF Electrotome.ttf");
Text score;
score.setFont(font);
score.setColor(Color::Black);
score.setString("0 0");
score.setCharacterSize(16);
score.setPosition(300, 50);
//loop
bool running = true;
while (running) //game loop
{
Event ev;
while (window.pollEvent(ev)) //event loop
{
switch (ev.type)
{
case Event::Closed: //quit on click x
running = false;
break;
case Event::KeyPressed:
if (ev.key.code == Keyboard::Escape) //quit on escape
{
running = false;
}
break;
}
}
//get that sweet info
Packet received;
socket.receive(received, other, port);
float player1pos[2];
float player2pos[2];
float ballpos[2];
received >> player1pos[0] >> player1pos[1] >> player2pos[0] >> player2pos[1] >>
ballpos[0] >> ballpos[1] >> score1 >> score2;
rect.setPosition(player1pos[0], player1pos[1]);
player2.setPosition(player2pos[0], player2pos[1]);
ball.setPosition(ballpos[0], ballpos[1]);
//scores
score.setString(NumberToString(score1) + " " + NumberToString(score2));
//handle player input
if (Keyboard::isKeyPressed(Keyboard::Up))
{
if (rect.getPosition().y >= 0) //keep in bounds
{
Packet p;
Uint16 direction = 1;
p << direction;
socket.send(p, other, port);
}
} else if (Keyboard::isKeyPressed(Keyboard::Down)){
if (player2.getPosition().y + player2.getSize().y <= 600) //keep in bounds
{
Packet p;
Uint16 direction = 2;
p << direction;
socket.send(p, other, port);
}
}
//draw everything
window.clear(Color::White);
window.draw(rect);
window.draw(player2);
window.draw(ball);
window.draw(score);
window.display();
}
return 0;
}
Server:
#include <SFML/Graphics.hpp>
#include <SFML/Network.hpp>
#include <cstdlib>
#include <ctime>
#include <sstream>
#include <iostream>
using namespace sf;
using namespace std;
template <typename T>
string NumberToString ( T Number )
{
stringstream ss;
ss << Number;
return ss.str();
}
int main()
{
cout << "Enter an IP address: ";
string address;
cin >> address;
IpAddress other(address);
if (other == IpAddress::None)
{
cout << "Error loading ip";
}
srand(time(NULL));
//window
RenderWindow window(VideoMode(800, 600), "Pong");
//player 1
RectangleShape rect(Vector2f(40, 130));
rect.setPosition(40, 50);
rect.setFillColor(Color::Blue);
rect.setOutlineThickness(4);
rect.setOutlineColor(Color::Black);
//player 2
RectangleShape player2(Vector2f(40, 130));
player2.setPosition(720, 50);
player2.setFillColor(Color(255, 0, 208));
player2.setOutlineThickness(4);
player2.setOutlineColor(Color::Black);
//ball
CircleShape ball(50, 50);
ball.setOrigin(ball.getRadius(), ball.getRadius());
ball.setPosition(400, 300);
ball.setFillColor(Color(255, 255, 0));
ball.setOutlineThickness(4);
ball.setOutlineColor(Color::Black);
float randX = (rand() % 5 + 1);
float randY = (rand() % 5 + 1);
Vector2f ballVelocity(randX/15, randY/15);
//score
Uint16 score1 = 0;
Uint16 score2 = 0;
//text
Font font;
font.loadFromFile("SF Electrotome.ttf");
Text score;
score.setFont(font);
score.setColor(Color::Black);
score.setString("0 0");
score.setCharacterSize(16);
score.setPosition(350, 50);
//network socket
UdpSocket socket;
short unsigned int sendPort = 54000;
if (!(socket.bind(sendPort) == Socket::Done))
{
cout << "Error";
}
socket.setBlocking(false);
//loop
bool running = true;
while (running) //game loop
{
Event ev;
while (window.pollEvent(ev)) //event loop
{
switch (ev.type)
{
case Event::Closed: //quit on click x
running = false;
break;
case Event::KeyPressed:
if (ev.key.code == Keyboard::Escape) //quit on escape
{
running = false;
}
break;
}
}
//handle player input
if (Keyboard::isKeyPressed(Keyboard::Up))
{
if (rect.getPosition().y >= 0) //keep in bounds
{
rect.move(0, -0.2);
}
} else if (Keyboard::isKeyPressed(Keyboard::Down)){
if (rect.getPosition().y + rect.getSize().y <= 600) //keep in bounds
{
rect.move(0, 0.2);
}
}
//handle player 2 input
Uint16 direction;
IpAddress sender;
Packet received;
if ((socket.receive(received, other, sendPort)) != Socket::Done)
{
cout << "Error receiving data";
}
received >> direction;
if (sender == other)
{
switch (direction)
{
case 1:
player2.move(0, -0.2);
break;
case 2:
player2.move(0, 0.2);
break;
}
}
//handle ball direction changes
if ((ball.getPosition().y - ball.getRadius() <= 0) || (ball.getPosition().y + ball.getRadius() >= 600)) { //top or bottom
ballVelocity.y *= -1;
} else if ((ball.getPosition().x + ball.getRadius() >= 800)) { //right edge
ballVelocity.x *= -1;
score1 += 1;
} else if (ball.getPosition().x - ball.getRadius() <= 0) { //left edge
ballVelocity.x *= -1;
score2 += 1;
}
ball.move(ballVelocity);
//handle player/ball collisions
FloatRect player1Box = rect.getGlobalBounds();
FloatRect ballBox = ball.getGlobalBounds();
FloatRect player2Box = player2.getGlobalBounds();
if (player1Box.intersects(ballBox) || player2Box.intersects(ballBox))
{
ballVelocity.x *= -1;
}
//set score
score.setString(NumberToString(score1) + " " + NumberToString(score2));
//send everything over
Packet p;
p << rect.getPosition().x << rect.getPosition().y << player2.getPosition().x << player2.getPosition().y <<
ball.getPosition().x << ball.getPosition().y << score1 << score2;
socket.send(p, other, sendPort);
//draw everything
window.clear(Color::White);
window.draw(rect);
window.draw(player2);
window.draw(ball);
window.draw(score);
window.display();
}
return 0;
}

Related

c++ sfml - changing the sprite isn't working

I was working on changing sprite in my SFML project but it just doesn't want to wark. I tried everything - pointers, refrences and normal string objects. You can see in debug console i printed out this->path that containted a path to file to change but when it left the function this->path was null (empty "" string). Can you please help me? Here's the code:
Player.cpp
#include "Player.h"
#include <iostream>
Player::Player(std::string path)
{
this->path = path;
texture.loadFromFile(path);
sprite.setTexture(texture);
sprite.setPosition(500.f, 700.f);
}
void Player::update()
{
// Movement //
//if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
//sprite.move(0.f, -velocity);
//if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
//sprite.move(0.f, velocity);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
sprite.move(-velocity, 0.f);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
sprite.move(velocity, 0.f);
// Movement //
float szerokosc = sprite.getGlobalBounds().width;
float wysokosc = sprite.getGlobalBounds().height;
// Collision with screen //
// Left
if (sprite.getPosition().x < 0.f)
sprite.setPosition(0.f, sprite.getPosition().y);
// Top
if (sprite.getPosition().y < 0.f)
sprite.setPosition(sprite.getPosition().x, 0.f);
// Right
if (sprite.getPosition().x + szerokosc > 1000)
sprite.setPosition(1000 - szerokosc, sprite.getPosition().y);
// Bottom
if (sprite.getPosition().y + wysokosc > 800)
sprite.setPosition(sprite.getPosition().x, 800 - wysokosc);
// Collision with screen //
}
sf::Sprite Player::getSprite()
{
return sprite;
}
bool Player::collides(Food obj)
{
if (sprite.getGlobalBounds().intersects(obj.getSprite().getGlobalBounds()))
return true;
else
return false;
}
void Player::changeSkin(std::string path)
{
this->path = path;
std::cout << "changeSkin(): *this->path" << this->path << std::endl;
std::cout << "changeSkin(): *path" << path << std::endl;
texture.loadFromFile(path);
sprite.setTexture(texture);
}
void Player::updateSkin()
{
std::cout << "updateSkin() *this->path: " << this->path << std::endl;
std::cout << "updateSkin() *path: " << path << std::endl;
}
Player::~Player()
{
//delete texture;
//delete sprite;
}
Player.h
#pragma once
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <time.h>
#include "Food.h"
#include "ShopItem.h"
class Player
{
sf::Texture texture;
sf::Sprite sprite;
std::string path;
bool has_skin = false;
float velocity = 5.f;
public:
explicit Player(std::string path);
~Player();
void update();
sf::Sprite getSprite();
bool collides(Food obj);
void changeSkin(std::string path);
void updateSkin();
};
main.cpp - main() has Player::updateSkin() call
int main()
{
RenderWindow window(VideoMode(1000, 800), "Tomczyszyn Eater v0.21 BETA");
window.setFramerateLimit(60);
srand(time(nullptr));
Texture bg_text;
bg_text.loadFromFile("Assets/Textures/bg.png");
Sprite bg{ bg_text };
Player player{ "Assets/Textures/player.png" };
time_t start{};
Event event;
Intro* intro = new Intro{window};
MainMenu* menu = new MainMenu{&window};
Shop shop(&window);
Game game{player, &window};
bool intro_deleted{ false };
bool menu_deleted{ false };
bool game_started{ false };
int menuState{ 2 };
while (window.isOpen())
{
while (window.pollEvent(event))
{
if (event.type == Event::EventType::Closed)
window.close();
if (Keyboard::isKeyPressed(Keyboard::Space) && game.gameRunning() == false)
{
start = time(nullptr);
game.runGame();
}
else if (Keyboard::isKeyPressed(Keyboard::Escape) && game.gameRunning() == false)
{
menuState = 0;
}
}
window.clear();
if (game.gameRunning() == true && menuState == 1) // Main Game
{
if (game_started == false)
{
start = time(nullptr);
game_started = true;
}
if (intro_deleted == false)
{
delete intro;
intro_deleted = true;
}
if (menu_deleted == false)
{
delete menu;
menu_deleted = true;
}
window.draw(bg);
game.drawMoney();
player.update();
window.draw(player.getSprite());
// Player::updateSkin() - source of the problem
player.updateSkin();
game.update();
game.draw();
if (time(nullptr) - start == 2)
{
start = time(nullptr);
int liczba = rand() % 6 + 1;
string file_name{ "Assets/Textures/food" + to_string(liczba) + ".png" };
Food* newFood = new Food{file_name};
game.spawn(newFood);
}
game.catched();
game.fell();
}
else if (menuState == 0) // Menu
{
if (menu_deleted == true) menu = new MainMenu{ &window };
menu->draw();
menu->update(menuState);
menu_deleted = false;
}
else if (menuState == -1) // Intro
{
start = time(nullptr);
intro->play();
if (intro->intro_started() == true) menuState = 0;
}
else if (menuState == 2) // Shop
{
shop.draw();
shop.backIfClicked(menuState);
shop.checkIfBought(player, game.balance());
game.drawMoney();
}
else
{
game.drawDeathScreen();
}
window.display();
}
}
Shop.cpp - checkIfBought() has Player::changeSkin() call
void Shop::checkIfBought(Player player, int& money)
{
for (int i = 0; i < skins.size(); i++)
{
if (isClicking(skins[i].getSprite()) == true)
{
skins[i].buy(money);
player.changeSkin(skins[i].getPath()); //Plaer::changeSkin() here - source of the problem
std::cout << skins[i].getPath() << std::endl;
}
}
for (int i = 0; i < bg_skins.size(); i++)
{
if (isClicking(bg_skins[i].getSprite()) == true)
{
bg_skins[i].buy(money);
player.changeSkin(bg_skins[i].getPath()); //Plaer::changeSkin() here - source of the problem
}
}
}
Debug Console output:
changeSkin(): this->path = Assets/Textures/skin1.png
changeSkin(): path = Assets/Textures/skin1.png
Assets/Textures/skin1.png
updateSkin() this->path = Assets/Textures/player.png
updateSkin() path = Assets/Textures/player.png
Sorry if you didn't understand my english is bad. I really do need help i've spent so much time on fixing this that i just gave up.

Speed up Serial Communication to Arduino c++

First time posting so I apologize.
I am trying to make a ball and beam project using a camera as the sensor and trying to get the serial communication to work.
The code currently works if I set the waitKey to (1000) but that is not fast enough for the project to work and was wanting some help speeding it up.
the output tends to only read the first string that I send then ignores anything else until the program is paused by holding the slider on the output screen for a couple of seconds.
I have tried to use a faster baud rate but the problem still persists.
This is the C++ code
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <vector>
#include "SerialPort.h"
#include <string.h>
#include <stdlib.h>
#include <string>
using namespace cv;
using namespace std;
//global for the image processing
Mat imgGray, imgCanny, imgBlur, imgDil;
Mat imgHSV, mask;
//int hmin = 53, smin = 102, vmin = 35; //green
//int hmax = 93, smax = 253, vmax = 169;
/* //green ball
int hmin = 41, smin = 93, vmin = 49;
int hmax = 93, smax = 243, vmax = 192; //p
*/
//purple ball
int hmin = 117, smin = 26, vmin = 63;
int hmax = 179, smax = 113, vmax = 169;
//global for the serial
char output[MAX_DATA_LENGTH];
char incomingData[MAX_DATA_LENGTH];
char port[] = " ////.//COM3";
int PosX;
// this will convert the output into the correct string because the "stoi" function doesn't work as intended
String intToString(int I)
{
String J;
if (I ==80 )
{
J ="80" ;
}
else if (I ==81 )
{
J = "81";
}
else if (I ==82 )
{
J = "82";
}
else if (I == 83)
{
J = "83";
}
else if (I ==84 )
{
J = "84";
}
else if (I == 85)
{
J = "85";
}
else if (I == 86)
{
J = "86";
}
else if (I ==87 )
{
J = "87";
}
else if (I ==88 )
{
J = "88";
}
else if (I ==89 )
{
J = "89";
}
else if (I ==90 )
{
J = "90";
}
else if (I ==91 )
{
J = "91";
}
else if (I == 92)
{
J = "92";
}
else if (I == 93)
{
J = "93";
}
else if (I == 94)
{
J = "94";
}
else if (I == 95)
{
J = "95";
}
else if (I == 96)
{
J = "96";
}
else if (I == 97)
{
J = "97";
}
else if (I == 98)
{
J = "98";
}
else if (I == 99)
{
J = "99";
}
else if (I == 100)
{
J = "100";
}
else if (I > 100)
{
J = "100";
}
else if (I < 80)
{
J = "80";
}
return (J);
}
// calculation to make the output between outMin and outMax taken from arduino website and transformed into calculation
int map(int x,int in_min, int in_max, int out_min, int out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// just the P of the PID controller
int PCONTROL(int xPos)
{
int P, Kp = 1, Proportional, error;
error = 240 - xPos; //240 is the middle of the camera and xPos is the current position
Proportional = Kp * error;
P = map(Proportional,-240,240,80,100); // calculation to make the output between 80&100 taken from arduino website and transformed into calculation
return (P);
}
// this is the function made to be the PID contol loop
int PIDCONTROL(int xPos)
{
int error_prior = 0, error, integral_prior = 0, Kp, Ki,
Kd, integral, derivative, proportional, outPut, PID_total, period = 50;
// I have set the Kp and Ki to zero when testing the P contoller
Kp = 1;
Ki = 0;
Kd = 0;
//error = (img.rows/2) - xPos; // 640 is the desired Value
error = (480/2) - xPos; // this is if I got the wrong part of the resolution
proportional = Kp * error;
integral = integral_prior + error;
derivative = (error - error_prior)/period;
PID_total = ((Kp * proportional) + (Ki * integral) + (Kd * derivative)); //this is the calculation for the PID controller
error_prior = error;
integral_prior = integral;
outPut = map(PID_total,-240,240,80,100);
cout << "output = " << outPut << endl;
return (outPut);
}
// this code will get the x and y values of the ball, draw a box around it and lable it
// this is what i need for the project
void getContours(Mat imgDil, Mat img)
{
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
string objectType;
string x, y;
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//drawContours(img, contours, -1, Scalar(255, 0, 255), 2); //draws everything
for (int i = 0; i < contours.size(); i++)
{
int area = contourArea(contours[i]);
//cout << area << endl; //displays the area of the ball
vector<vector<Point>>conPoly(contours.size());
vector<Rect>boundRect(contours.size());
if (area > 1000 && area < 5000) //filters out smaller and larger objects
{
float peri = arcLength(contours[i], true);
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
drawContours(img, contours, i, Scalar(255, 0, 255), 2); // with a filter applyed
boundRect[i] = boundingRect(conPoly[i]); //draw a box around the ball
rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
putText(img, "Ball", { boundRect[i].x,boundRect[i].y - 5 }, FONT_HERSHEY_PLAIN, 1, Scalar(0, 69, 255), 2); //display text above the box saying ball
//cout << "X = " << boundRect[i].x << endl;// << "Y = " << boundRect[i].y << endl; //print the x and y values
PosX = boundRect[i].x;
}
}
}
void main()
{
VideoCapture cap(0);
Mat img;
string xposSrg;
//int TempData;
Mat Reimg, Reimg2;
string data;
//char TrackbarName[50];
const int alpha_slider_max = 100;
int xPos, preData = 0;
createTrackbar("Hue Min", "Trackbars", &hmin, 179);
SerialPort arduino(port);
if (arduino.isConnected()) {
cout << "Connection" << endl << endl;
}
else {
cout << "Error Connecting" << endl << endl;
}
while (arduino.isConnected()) // no point do it if not connected
{
cap.read(img);
//prepocessing
Mat imgGray, imgCanny, imgBlur,Reimg,Reimg2;
//cvtColor(img, imgGray, COLOR_BGR2GRAY);
//pyrDown(img, Reimg, Size(img.cols / 2, img.rows / 2)); // downsample for faster processing
cvtColor(img, imgHSV, COLOR_BGR2HSV); // get the big wierd colours
Scalar lower(hmin, smin, vmin);
Scalar upper(hmax, smax, vmax);
inRange(imgHSV, lower, upper, mask); // make only the green ball visible
GaussianBlur(mask, imgBlur, Size(3, 3), 3, 0);
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDil, kernel);
getContours(imgDil, img); // use only the visible green to the contours fuction
//displaying processes used in debuging
/*
imshow("Ball", img);
imshow("Gray", imgGray);
imshow("Blur", imgBlur);
imshow("Canny", imgCanny);
imshow("Dil", imgDil);
*/
//cout << img.rows << endl;;
//display the orginal img with the contors drawn on to it
imshow("img", img);
// do the PID controller calculations
preData = PIDCONTROL(PosX);
//send the data to the arduino
data = intToString(preData);
cout << "Data =" << data << "." << endl;
char* charArray = new char[data.size() +1];
copy(data.begin(), data.end(), charArray);
charArray[data.size()] = '\n';
arduino.writeSerialPort(charArray, MAX_DATA_LENGTH);
arduino.readSerialPort(output, MAX_DATA_LENGTH);
cout << " Arduino >> " << output << "." << endl;
delete[] charArray;
waitKey(1); //work as fast as possible
// turns out this library can only output every second
}
}
Here is the Arduino code
#include <Servo.h>
Servo myservo;
String info;
void setup() {
Serial.begin(9600);
//pinMode(led,OUTPUT);
myservo.attach(9); // attaches the servo on pin 9 to the servo object
myservo.write(90); // set the starting position to 90 degrees(flat)
}
// this is the one for on using 10 degrees
void loop()
{
if (Serial.available() >0)
{
info = Serial.readStringUntil('\n');
}
Serial.flush();
//check what the imput is then change the servo level (every 10 degrees) and output the level
// checking the string other ways caused errors dunno why
if (info.equals("0"))
{
Serial.println("Not Receiving Data");
myservo.write(90);
}
else if (info.equals("80"))
{
Serial.println("80");
myservo.write(80);
}
else if (info.equals("81"))
{
Serial.println("81");
myservo.write(81);
}
else if (info.equals("82"))
{
Serial.println("82");
myservo.write(82);
}
else if (info.equals("83"))
{
Serial.println("83");
myservo.write(83);
}
else if (info.equals("84"))
{
Serial.println("84");
myservo.write(84);
}
else if (info.equals("85"))
{
Serial.println("85");
myservo.write(85);
}
else if (info.equals("86"))
{
Serial.println("86");
myservo.write(86);
}
else if (info.equals("87"))
{
Serial.println("87");
myservo.write(87);
}
else if (info.equals("88"))
{
Serial.println("88");
myservo.write(88);
}
else if (info.equals("89"))
{
Serial.println("89");
myservo.write(89);
}
else if (info.equals("90"))
{
Serial.println("90");
myservo.write(90);
}
else if (info.equals("91"))
{
Serial.println("91");
myservo.write(91);
}
else if (info.equals("92"))
{
Serial.println("92");
myservo.write(92);
}
else if (info.equals("93"))
{
Serial.println("93");
myservo.write(93);
}
else if (info.equals("94"))
{
Serial.println("94");
myservo.write(94);
}
else if (info.equals("95"))
{
Serial.println("95");
myservo.write(95);
}
else if (info.equals("96"))
{
Serial.println("96");
myservo.write(96);
}
else if (info.equals("97"))
{
Serial.println("97");
myservo.write(97);
}
else if (info.equals("98"))
{
Serial.println("98");
myservo.write(98);
}
else if (info.equals("99"))
{
Serial.println("99");
myservo.write(99);
}
else if (info.equals("100"))
{
Serial.println("100");
myservo.write(100);
}
}
Here is the headers file taken from https://www.youtube.com/watch?v=8BWjyZxGr5o
#ifndef SERIALPORT_H
#define SERIALPORT_H
#define ARDUINO_WAIT_TIME 2000
#define MAX_DATA_LENGTH 256
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
class SerialPort
{
private:
HANDLE handler;
bool connected;
COMSTAT status;
DWORD errors;
public:
SerialPort(char* portName);
~SerialPort();
int readSerialPort(char* buffer, unsigned int buf_size);
bool writeSerialPort(char* buffer, unsigned int buf_size);
bool isConnected();
};
#endif // SERIALPORT_H
#include "SerialPort.h"
SerialPort::SerialPort(char* portName)
{
this->connected = false;
this->handler = CreateFileA(static_cast<LPCSTR>(portName),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (this->handler == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
printf("ERROR: Handle was not attached. Reason: %s not available\n", portName);
}
else
{
printf("ERROR!!!");
}
}
else {
DCB dcbSerialParameters = { 0 };
if (!GetCommState(this->handler, &dcbSerialParameters)) {
printf("failed to get current serial parameters");
}
else {
dcbSerialParameters.BaudRate = CBR_9600;
dcbSerialParameters.ByteSize = 8;
dcbSerialParameters.StopBits = ONESTOPBIT;
dcbSerialParameters.Parity = NOPARITY;
dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;
if (!SetCommState(handler, &dcbSerialParameters))
{
printf("ALERT: could not set Serial port parameters\n");
}
else {
this->connected = true;
PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
Sleep(ARDUINO_WAIT_TIME);
}
}
}
}
SerialPort::~SerialPort()
{
if (this->connected) {
this->connected = false;
CloseHandle(this->handler);
}
}
int SerialPort::readSerialPort(char* buffer, unsigned int buf_size)
{
DWORD bytesRead;
unsigned int toRead = 0;
ClearCommError(this->handler, &this->errors, &this->status);
if (this->status.cbInQue > 0) {
if (this->status.cbInQue > buf_size) {
toRead = buf_size;
}
else toRead = this->status.cbInQue;
}
if (ReadFile(this->handler, buffer, toRead, &bytesRead, NULL)) return bytesRead;
return 0;
}
bool SerialPort::writeSerialPort(char* buffer, unsigned int buf_size)
{
DWORD bytesSend;
if (!WriteFile(this->handler, (void*)buffer, buf_size, &bytesSend, 0)) {
ClearCommError(this->handler, &this->errors, &this->status);
return false;
}
else return true;
}
bool SerialPort::isConnected()
{
return this->connected;
}

Playing sine soundwave with SDL2 - noise / scratch issue

My goal is to create an SDL window plotting different waveforms and playing an indefinite sound of this wave. By pressing specific keys, the parameters of the wave, like the amplitude, frequency or waveform can be modified.
The problem is that even a simple sine wave which looks nice when plotted, sounds noisy. I don't understand why.
Code:
#include "Graph.h"
#include <thread>
#include <iostream>
#include <sstream>
#include <string>
int main(int argc, char* argv[]){
Graph* g = new Graph();
int i;
std::cin >> i;
return 0;
}
int graphThreadFunc(void *pointer){
Graph* grid = (Graph*)pointer;
grid->init();
return 0;
}
// SDL calls this function whenever it wants its buffer to be filled with samples
void SDLAudioCallback(void *data, Uint8 *buffer, int length){
uint8_t *stream = (uint8_t*)buffer;
Graph* graph = (Graph*)data;
for (int i = 0; i <= length; i++){
if (graph->voice.audioLength <= 0)
stream[i] = graph->getSpec()->silence; // 128 is silence in a uint8 stream
else
{
stream[i] = graph->voice.getSample();
graph->voice.audioPosition++;
// Fill the graphBuffer with the first 1000 bytes of the wave for plotting
if (graph->graphPointer < 999)
graph->graphBuffer[graph->graphPointer++] = stream[i];
}
}
}
Graph::Graph()
{
// spawn thread
SDL_Thread *refresh_thread = SDL_CreateThread(graphThreadFunc, NULL, this);
}
SDL_AudioSpec* Graph::getSpec(){
return &this->spec;
}
void Graph::init()
{
// Init SDL & SDL_ttf
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_zero(desiredDeviceSpec);
desiredDeviceSpec.freq = 44100; // Sample Rate
desiredDeviceSpec.format = AUDIO_U8; // Unsigned 8-Bit Samples
desiredDeviceSpec.channels = 1; // Mono
desiredDeviceSpec.samples = 2048; // The size of the Audio Buffer (in number of samples, eg: 2048 * 1 Byte (AUDIO_U8)
desiredDeviceSpec.callback = SDLAudioCallback;
desiredDeviceSpec.userdata = this;
dev = SDL_OpenAudioDevice(NULL, 0, &desiredDeviceSpec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if (dev == 0) {
printf("\nFailed to open audio: %s\n", SDL_GetError());
}
else {
SDL_PauseAudioDevice(dev, 1); /* pause! */
SDL_PauseAudio(1);
}
// Create an application window with the following settings:
window = SDL_CreateWindow(
WINDOW_TITLE.c_str(), // window title
SDL_WINDOWPOS_UNDEFINED, // initial x position
SDL_WINDOWPOS_UNDEFINED, // initial y position
WINDOW_WIDTH, // width, in pixels
WINDOW_HEIGHT, // height, in pixels
SDL_WINDOW_SHOWN // flags - see below
);
// Check if the window was successfully created
if (window == NULL) {
// In case the window could not be created...
printf("Could not create window: %s\n", SDL_GetError());
return;
}
else{
voice.waveForm = Graph::Voice::WaveForm::SINE;
voice.amp = 120;
voice.frequency = 440;
SDL_PauseAudioDevice(dev, 1); // play
graphPointer = 0;
voice.audioLength = 44100;
voice.audioPosition = 0;
SDL_PauseAudioDevice(dev, 0); // play
SDL_Delay(200);
drawGraph();
mainLoop();
return;
}
}
void Graph::mainLoop()
{
while (thread_exit == 0){
SDL_Event event;
bool hasChanged = false;
while (SDL_PollEvent(&event)) {
switch (event.type)
{
case SDL_KEYDOWN:
{
hasChanged = true;
if (event.key.keysym.scancode == SDL_SCANCODE_SPACE){
//pause_thread = !pause_thread;
switch (voice.waveForm){
case Voice::SINE:
{
voice.waveForm = Graph::Voice::WaveForm::TRIANGLE;
break;
}
case Voice::TRIANGLE:
{
voice.waveForm = Graph::Voice::WaveForm::RECT;
break;
}
case Voice::RECT:
{
voice.waveForm = Graph::Voice::WaveForm::SAWTOOTH;
break;
}
case Voice::SAWTOOTH:
{
voice.waveForm = Graph::Voice::WaveForm::SINE;
break;
}
default:
break;
}
}
else if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE){
exit();
}
else if (event.key.keysym.scancode == SDL_SCANCODE_RETURN){
}
else if (event.key.keysym.scancode == SDL_SCANCODE_LEFT){
voice.frequency -= 2;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT){
voice.frequency += 2;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_UP){
voice.amp += 2;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_DOWN){
voice.amp -= 2;
}
else{
}
break;
}
case SDL_QUIT:
{
exit();
return;
break;
}
default: /* unhandled event */
break;
}
}
if (!pause_thread && hasChanged)
{
//SDL_PauseAudioDevice(dev, 1); // play
graphPointer = 0;
voice.audioLength = 44100;
voice.audioPosition = 0;
SDL_PauseAudioDevice(dev, 0); // play
SDL_Delay(200);
drawGraph();
}
//voice.waveForm = Voice::WaveForm::TRIANGLE;
//SDL_Delay(n); // delay the program to prevent the voice to be overridden before it has been played to the end
//SDL_PauseAudioDevice(dev, 1); // pause
SDL_Delay(REFRESH_INTERVAL);
//SDL_PauseAudioDevice(dev, 1); // pause
}
return;
}
void Graph::drawGraph()
{
SDL_Renderer *renderer = SDL_GetRenderer(window);
if (renderer == nullptr)
renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED);
// Set background color
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
// Clear winow
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 22, 22, 22, 255);
for (int x = 0; x < WINDOW_WIDTH; x++){
uint8_t y = graphBuffer[x];
SDL_RenderDrawPoint(renderer, x, WINDOW_HEIGHT - y);
}
SDL_RenderPresent(renderer);
return;
}
void Graph::exit(){
thread_exit = 1;
// Close and destroy the window
SDL_DestroyWindow(window);
// Clean up
SDL_Quit();
}
uint8_t Graph::Voice::getSample(){
switch (waveForm){
case SINE:
{
float sineStep = 2 * M_PI * audioPosition * frequency / 44100;
return (amp * sin(sineStep)) + 128;
break;
}
case RECT:
break;
case SAWTOOTH:
break;
case TRIANGLE:
break;
default:
return 0;
}
}
And the header file:
#ifndef GRAPH_H
#define GRAPH_H
#include "SDL.h"
#include "SDL_audio.h"
#include <stdio.h>
#include <cmath>
#include <string>
#include <stack>
/* Constants */
const int REFRESH_INTERVAL = 50; // mseconds
const int WINDOW_WIDTH = 1000;
const int WINDOW_HEIGHT = 255;
const std::string WINDOW_TITLE = "Wave Graph";
class Graph
{
private:
SDL_Window *window; // Declare a pointer
// SDL audio stuff
SDL_AudioSpec desiredDeviceSpec;
SDL_AudioSpec spec;
SDL_AudioDeviceID dev;
int thread_exit = 0;
bool pause_thread = false;
public:
Graph();
void init();
void mainLoop();
void drawGraph();
void exit();
SDL_AudioSpec* getSpec();
struct Voice{
int frequency; // the frequency of the voice
int amp; // the amplitude of the voice
int audioLength; // number of samples to be played, eg: 1.2 seconds * 44100 samples per second
int audioPosition = 0; // counter
enum WaveForm{
SINE = 0, RECT = 1, SAWTOOTH = 2, TRIANGLE = 3
} waveForm;
uint8_t getSample();
} voice;
int graphPointer = 0;
uint8_t graphBuffer[1000];
};
#endif
Your SDLAudioCallback() is writing an extra byte off the end of buffer:
void SDLAudioCallback(void *data, Uint8 *buffer, int length)
{
...
for (int i = 0; i <= length; i++)
// ^^ huh?
{
...
}
}
Changing the <= to just < fixes the crackles on my system.
Generally C-style "byte pointer + length" APIs expect a left-closed, right-open interval: [0, length). I.e., you can access buffer[length - 1] but not buffer[length].

How to press multiple keys at the same time using events in real-time? (Allegro 5)

This is a problem that haunts me for years.
Here's my game.h and game.cpp files:
game.h
#ifndef GAME_H_INCLUDED
#define GAME_H_INCLUDED
#include "init.h"
ALLEGRO_BITMAP *load_bmp(path *s);
struct Actor {
const char *path;
ALLEGRO_BITMAP *bmp;
int x;
int y;
int speed;
};
void init_game_bitmaps();
void draw_game_bitmaps();
extern map<string, bool> key_states;
void init_key_states();
void check_states();
void control_actor(Actor *target, int speed);
extern Actor player;
#endif // GAME_H_INCLUDED
game.cpp
#include "game.h"
ALLEGRO_BITMAP *load_bmp(path *s) {
ALLEGRO_BITMAP *bmp = nullptr;
bmp = al_load_bitmap(s);
if (!bmp) {
al_show_native_message_box(display,
"Fatal Error!",
"Failed to load: " ,
s,
NULL,
ALLEGRO_MESSAGEBOX_ERROR);
al_destroy_display(display);
return nullptr;
}
return bmp;
}
map<string, bool> key_states;
void init_key_states() {
key_states["UP"] = false;
key_states["DOWN"] = false;
key_states["LEFT"] = false;
key_states["RIGHT"] = false;
}
void check_states() {
auto key = e.keyboard.keycode;
for (auto it = key_states.begin(); it != key_states.end(); ++it) {
if (e.type == ALLEGRO_EVENT_KEY_DOWN) {
if (key == ALLEGRO_KEY_UP) {
if (it->first == "UP") {
it->second = true;
}
}
if (key == ALLEGRO_KEY_DOWN) {
if (it->first == "DOWN") {
it->second = true;
}
}
if (key == ALLEGRO_KEY_LEFT) {
if (it->first == "LEFT") {
it->second = true;
}
}
if (key == ALLEGRO_KEY_RIGHT) {
if (it->first == "RIGHT") {
it->second = true;
}
}
} else if (e.type == ALLEGRO_EVENT_KEY_UP) {
if (key == ALLEGRO_KEY_UP) {
if (it->first == "UP") {
it->second = false;
}
}
if (key == ALLEGRO_KEY_DOWN) {
if (it->first == "DOWN") {
it->second = false;
}
}
if (key == ALLEGRO_KEY_LEFT) {
if (it->first == "LEFT") {
it->second = false;
}
}
if (key == ALLEGRO_KEY_RIGHT) {
if (it->first == "RIGHT") {
it->second = false;
}
}
}
cout << it->first << " : " << it->second << endl;
}
}
void control_actor(Actor *target, int speed) {
if (key_states["UP"]) {
target->y -= speed;
}
if (key_states["DOWN"]) {
target->y += speed;
}
if (key_states["LEFT"]) {
target->x -= speed;
}
if (key_states["RIGHT"]) {
target->x += speed;
}
}
Actor player = {
"GFX\\player_up.png",
nullptr,
(SCREEN_WIDTH / 2) - (ACTOR_SIZE / 2),
(SCREEN_HEIGHT / 2) - (ACTOR_SIZE / 2),
8};
void init_game_bitmaps() {
player.bmp = load_bmp(player.path);
}
void draw_game_bitmaps() {
al_draw_bitmap(player.bmp, player.x, player.y, 0);
al_flip_display();
}
Now here's my main file:
main.cpp
#include "init.h"
#include "game.h"
int main(int argc, char **argv){
init_all();
register_all();
init_game_bitmaps();
init_key_states();
while (running) {
draw_game_bitmaps();
al_wait_for_event(event_queue, &e);
if (e.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
running = false;
}
check_states();
control_actor(&player, player.speed);
if (e.type == ALLEGRO_EVENT_KEY_DOWN) {
if (e.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
running = false;
}
if (e.keyboard.keycode == ALLEGRO_KEY_ENTER) {
cout << "It works!";
}
}
}
destroy_all();
return 0;
}
As you can see, I have a std::map that stores key states (One for each arrow of the keyboard), and then I have a procedure called check_states(), that iterate over all the states at each main loop, and set them to true if their respective arrows are pressed (down), and to false when they are released.
The problem:
If I press UP and keep it holding, and then I press LEFT (Without releasing the UP key), the player will move diagonally. Nevertheless, if I release the LEFT, while still holding the UP key, the player will stop, and the state for UP will be true (And I see this because I'm couting it).
Theory
The al_wait_for_event() waits until the event queue specified is non-empty (Which means that when I press the UP key, it copies the event to e, and then when I press the LEFT, it must cancel UP and assign a new event to e, thus creating the unpleasant LAG when I press more than one key at once). Having that in mind, I've concluded: Well, I could have at least FIVE separate event_queues, and FIVE different "event objects". I've managed to create a vector of event_queues and of events and to iterate over both of them at each main loop while passing each event to its respective event_queue AND THE PROBLEM PERSISTED.
SO, how can I press more than one key in real-time? By real-time I mean real real-time, without lags, without any key canceling each other. The answer is key states? Why? How can I do it using events? Is it possible at all?.
EDIT:
I've changed my control_actor() procedure in order to use al_key_down() instead of checking for events, here's its code:
void control_actor(ALLEGRO_KEYBOARD_STATE *key, Actor *target, int speed) {
if (al_key_down(key, ALLEGRO_KEY_UP)) {
target->y -= speed;
cout << "UP" << endl;
}
if (al_key_down(key, ALLEGRO_KEY_DOWN)) {
target->y += speed;
cout << "DOWN" << endl;
}
if (al_key_down(key, ALLEGRO_KEY_LEFT)) {
target->x -= speed;
cout << "LEFT" << endl;
}
if (al_key_down(key, ALLEGRO_KEY_RIGHT)) {
target->x += speed;
cout << "RIGHT" << endl;
}
}
And the new main.cpp:
#include "init.h"
#include "game.h"
int main(int argc, char **argv){
init_all();
register_all();
init_game_bitmaps();
ALLEGRO_KEYBOARD_STATE key;
while (running) {
draw_game_bitmaps();
al_wait_for_event(event_queue, &e);
al_get_keyboard_state(&key);
control_actor(&key, &player, player.speed);
if (e.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
running = false;
}
if (e.type == ALLEGRO_EVENT_KEY_DOWN) {
if (e.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
running = false;
}
if (e.keyboard.keycode == ALLEGRO_KEY_ENTER) {
cout << "It works!";
}
}
}
destroy_all();
return 0;
}
The post on the allegro forums linked in the comment is from 2002, and that code does not work anymore on Allegro 5. So I've checked the docs, and I'll tell you: THE PROBLEM PERSISTED. The EXACT same thing happens. One arrow cancels the other and the player stops moving for a while, as soon as I press another arrow at the same time.
The Basic Keyboard Example on the allegro wiki may be of more help than that old post.
There is no need to manage multiple event queues here. Every key press and release should get pushed into the queue -- you just need to make sure you process every event in the queue.
Basically, you want a main loop that:
Processes every event currently in the queue
Updates the game state
Redraws the screen
Here is an example I drafted up:
#include <stdio.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
const int SPEED = 5;
const float FPS = 60;
int main(int argc, char **argv) {
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
ALLEGRO_TIMER *timer = NULL;
int x = 0, y = 0; // position
int vx = 0, vy = 0; // velocity
// initialize everything we need -- error checking omitted for brevity
al_init();
al_install_keyboard();
al_init_primitives_addon();
display = al_create_display(640, 480);
event_queue = al_create_event_queue();
timer = al_create_timer(1.0 / FPS);
al_register_event_source(event_queue, al_get_keyboard_event_source());
al_register_event_source(event_queue, al_get_timer_event_source(timer));
al_start_timer(timer);
bool done = false;
while(!done) {
bool redraw = false;
// process events until queue is empty
while(!al_is_event_queue_empty(event_queue)) {
ALLEGRO_EVENT ev;
al_wait_for_event(event_queue, &ev);
switch(ev.type) {
case ALLEGRO_EVENT_KEY_DOWN:
switch(ev.keyboard.keycode) {
case ALLEGRO_KEY_W:
vy -= SPEED; // add upward velocity
break;
case ALLEGRO_KEY_S:
vy += SPEED; // add downward velocity
break;
case ALLEGRO_KEY_A:
vx -= SPEED; // add leftward velocity
break;
case ALLEGRO_KEY_D:
vx += SPEED; // add leftward velocity
break;
case ALLEGRO_KEY_ESCAPE:
done = true;
break;
}
break;
case ALLEGRO_EVENT_KEY_UP:
switch(ev.keyboard.keycode) {
case ALLEGRO_KEY_W:
vy += SPEED; // remove upward velocity
break;
case ALLEGRO_KEY_S:
vy -= SPEED; // remove downward velocity
break;
case ALLEGRO_KEY_A:
vx += SPEED; // remove leftward velocity
break;
case ALLEGRO_KEY_D:
vx -= SPEED; // remove leftward velocity
break;
}
break;
case ALLEGRO_EVENT_TIMER:
redraw = true; // time for next frame
break;
}
}
// got through all the events this loop -- redraw if necessary
if (redraw) {
// move circle
x += vx;
y += vy;
// draw circle
al_clear_to_color(al_map_rgb(0, 0, 0));
al_draw_filled_circle(x, y, 20, al_map_rgb(0, 0, 255));
al_flip_display();
}
}
al_destroy_display(display);
return 0;
}
Note the while(!al_is_event_queue_empty(event_queue)). This ensures that we don't miss any event before moving on to the update loop.
If you try running the example, the circle should respond appropriately to any combination of the WASD keys.
If you hold S+D, it will move diagonally right and down.
Release S, and it will continue moving right, but not down.
Hope this helps!

C++ - Can't get if statement to work using states

I'm currently creating a project which involves switching between different game states such as main menu to play state and play state to battle state etc. I'm currently struggling with getting my battle state to only draw the enemy the player collides with inside the play state. I tried doing this using a very simply "enemyNo =;" where the first enemy was 1, second was 2, and so on. then in battle state, tell it "if(enemyNo =1) then draw enemy 1 and it's corresponding stats. Can't for the life of me figure it out, pretty sure im not inheriting the enemyNo value across states properly. Any and all help would be appreciated.
StatePlay.cpp
#include "StatePlay.h"
#include <iostream>
// stringstream and string
#include <sstream>
#include <string>
#include "Game.h"
StatePlay::StatePlay(void)
{
}
StatePlay::~StatePlay(void)
{
}
void StatePlay::init(Game &context)
{
std::cout << "Initialising play state." << std::endl;
player = new Rect(0.0f, 0.0f, 0.15f, 0.15f);
//player stats
health = 10;
maxHealth = 10;
strength = 10;
speed = 10;
dollars = 0;
noTargets = false;
targetCount = 5;
bruteCount = 3;
raiderCount = 1;
//target
for(int i = 1; i <=5; i++)
{
target[i] = new Rect();
target[i]->setX((float)rand()/RAND_MAX - 0.75f);
target[i]->setY((float)rand()/RAND_MAX - 0.75f);
targetAlive[i] = true;
}
//brute
for(int i = 1; i <=3; i++)
{
brute[i] = new Rect();
brute[i]->setX((float)rand()/RAND_MAX - 0.75f);
brute[i]->setY((float)rand()/RAND_MAX - 0.75f);
bruteAlive[i] = true;
}
//raider
for(int i = 1; i <=1; i++)
{
raider[i] = new Rect();
raider[i]->setX((float)rand()/RAND_MAX - 0.75f);
raider[i]->setY((float)rand()/RAND_MAX - 0.75f);
raiderAlive[i] = true;
}
score = 0;
}
void StatePlay::enter()
{
std::cout << "Entering play state. Press ESCAPE to return to menu" << std::endl;
}
void StatePlay::exit()
{
}
void StatePlay::draw(SDL_Window * window)
{
// Replace this with OpenGL draw calls to draw the main menu screen
TTF_Font *textFont = TTF_OpenFont("MavenPro-Regular.ttf", 24);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1.0,1.0,1.0);
player->draw();
std::stringstream strStream2;
std::stringstream strStream3;
strStream2 << "Player" << std::endl;
strStream3 << "Target" << std::endl;
Label :: displayString(player->x() + player->w()/2.0,player->y() + player->h(), strStream2.str().c_str(), textFont);
//enemy
for(int i = 1; i <=5; i++)
{
if(targetAlive[i] == true)
{
Label :: displayString(target[i]->x() + target[i]->w()/2.0,target[i]->y() + target[i]->h(), strStream3.str().c_str(), textFont);
glColor3f(1.0,0.0,0.0);
targetAlive[i] = target[i];
target[i]->draw();
}
}
std::stringstream strStream4;
strStream4 << "Brute" << std::endl;
//brute
for(int i = 1; i <=3; i++)
{
if(bruteAlive[i] == true)
{
Label :: displayString(brute[i]->x() + brute[i]->w()/2.0,brute[i]->y() + brute[i]->h(), strStream4.str().c_str(), textFont);
glColor3f(0.6,0.3,0.1);
bruteAlive[i] = brute[i];
brute[i]->draw();
}
}
std::stringstream strStream6;
strStream6 << "Raider" << std::endl;
//raider
for(int i = 1; i <=1; i++)
{
if(raiderAlive[i] == true)
{
Label :: displayString(raider[i]->x() + raider[i]->w()/2.0,raider[i]->y() + raider[i]->h(), strStream6.str().c_str(), textFont);
glColor3f(0.2,0.9,0.4);
raiderAlive[i] = raider[i];
raider[i]->draw();
}
}
//information
std::stringstream strStream;
std::stringstream strStream1;
strStream << "Score:" << score;
strStream1 << "Health:" << health;
Label :: displayString(-0.9,-0.9, strStream.str().c_str(), textFont);
Label :: displayString(-0.4,-0.9, strStream1.str().c_str(), textFont);
SDL_GL_SwapWindow(window); // swap buffers
}
void StatePlay::handleSDLEvent(SDL_Event const &sdlEvent, Game &context)
{
if (sdlEvent.type == SDL_KEYDOWN)
{
//std::cout << "Scancode: " << sdlEvent.key.keysym.scancode ;
//std::cout << ", Name: " << SDL_GetKeyName( sdlEvent.key.keysym.sym ) << std::endl;
switch( sdlEvent.key.keysym.sym )
{
case SDLK_UP:
case 'w': case 'W':
player->setY(player->y() + 0.05f);
break;
case SDLK_DOWN:
case 's': case 'S':
player->setY(player->y() - 0.05f);
break;
case SDLK_LEFT:
case 'a': case 'A':
player->setX(player->x() - 0.05f);
break;
case SDLK_RIGHT:
case 'd': case 'D':
player->setX(player->x() + 0.05f);
break;
default:
break;
}
}
if (sdlEvent.type == SDL_KEYDOWN)
{
//std::cout << "Scancode: " << sdlEvent.key.keysym.scancode ;
//std::cout << ", Name: " << SDL_GetKeyName( sdlEvent.key.keysym.sym ) << std::endl;
switch( sdlEvent.key.keysym.sym )
{
case SDLK_ESCAPE:
context.setState(context.getMainMenuState());
default:
break;
}
}
//target
for(int i = 1; i <=5; i++)
{
if ( (target[i]->x() >= player->x()) && (target[i]->x()+target[i]->w() <= player->x()+player->w()) // cursor surrounds target in x
&& (target[i]->y() >= player->y()) && (target[i]->y()+target[i]->h() <= player->y()+player->h() ) && targetAlive[i] == true ) // cursor surrounds target in y
{
score += 100;
enemyNo = 1;
context.setState(context.getBattleState());
delete target[i];
targetAlive[i] = false;
targetCount -= 1;
}
}
//brute
for(int i = 1; i <=3; i++)
{
if ( (brute[i]->x() >= player->x()) && (brute[i]->x()+brute[i]->w() <= player->x()+player->w()) // cursor surrounds target in x
&& (brute[i]->y() >= player->y()) && (brute[i]->y()+brute[i]->h() <= player->y()+player->h() ) && bruteAlive[i] == true ) // cursor surrounds target in y
{
score += 100;
enemyNo = 2;
context.setState(context.getBattleState());
delete brute[i];
bruteAlive[i] = false;
bruteCount -= 1;
}
}
//raider
for(int i = 1; i <=1; i++)
{
if ( (raider[i]->x() >= player->x()) && (raider[i]->x()+raider[i]->w() <= player->x()+player->w()) // cursor surrounds target in x
&& (raider[i]->y() >= player->y()) && (raider[i]->y()+raider[i]->h() <= player->y()+player->h() ) && raiderAlive[i] == true ) // cursor surrounds target in y
{
score += 100;
enemyNo = 3;
context.setState(context.getBattleState());
delete raider[i];
raiderAlive[i] = false;
raiderCount -= 1;
}
}
}
StatePlay.h
#ifndef STATE_PLAY
#define STATE_PLAY
#include "gameState.h"
#include "Label.h"
// C stdlib and C time libraries for rand and time functions
#include <cstdlib>
#include <ctime>
#include "rect.h"
class StatePlay : public GameState
{
public:
StatePlay(void);
~StatePlay(void);
void init(Game &context);
void draw(SDL_Window * window);
void handleSDLEvent(SDL_Event const &sdlEvent, Game &context);
void enter();
void exit();
int enemyNo;
int targetCount;
private:
//player Stats
int score;
int health;
int maxHealth;
int strength;
int speed;
int dollars;
int getHealth() {return health;}
int getMaxHealth() { return maxHealth;}
int getStrength() { return strength;}
int getSpeed() { return speed;}
int getDollars() { return dollars; }
void setHealth(int newHealth) { health = newHealth; }
void damageTaken(int damage) { health = health - damage; }
void setMaxHealth(int newHealth) { maxHealth = maxHealth + newHealth; }
void setStrength(int newStrength) { strength = strength+newStrength; }
void setSpeed(int newSpeed) { speed = speed+newSpeed; }
void setDollars(int newDollars) { dollars = dollars+newDollars; }
// Another *strong* candidate for creating a new class type
// is character/agent, to encapsulate player and target data,
// potentially to include an Agent::draw() function and
// perhaps also generalise collision detection
// This is left as a further exercise
float xpos;
float ypos;
float xsize;
float ysize;
//target
float targetXPos;
float targetYPos;
float targetXSize;
float targetYSize;
bool targetAlive[5];
bool noTargets;
//brute
float bruteXPos;
float bruteYPos;
float bruteXSize;
float bruteYSize;
int bruteCount;
bool bruteAlive[5];
bool noBrutes;
//raider
float raiderXPos;
float raiderYPos;
float raiderXSize;
float raiderYSize;
int raiderCount;
bool raiderAlive[1];
bool noRaider;
clock_t lastTime; // clock_t is an integer type
clock_t currentTime; // use this to track time between frames
Label * playerLabel;
Label * targetLabel;
Label * bruteLabel;
Label * raiderLabel;
TTF_Font * textFont; // SDL type for True-Type font rendering
Rect * player;
Rect * target[5];
Rect * brute[3];
Rect * raider[1];
};
#endif
StateBattle.cpp
#include "StateBattle.h"
#include <iostream>
#include "Game.h"
#include "StatePlay.h"
#include "rect.h"
StateBattle::StateBattle(void)
{
}
StateBattle::~StateBattle(void)
{
}
void StateBattle::init(Game &context)
{
fighting = false;
}
void StateBattle::enter()
{
std::cout << "Entering main menu state: Press RETURN to play or ESCAPE to quit " << std::endl;
}
void StateBattle::exit()
{
}
void StateBattle::draw(SDL_Window * window)
{
glClearColor(0.0f,0.0f,0.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1.0,1.0,1.0);
TTF_Font *textFont = TTF_OpenFont("MavenPro-Regular.ttf", 24);
std::stringstream strStream;
strStream << "FIGHT!" <<std::endl;
Label :: displayString(-0.8 ,0.8, strStream.str().c_str(),textFont);
//player
Rect * player;
player = new Rect(-0.4f, 0.0f, 0.15f, 0.15f);
player ->draw();
std::stringstream playerStrStream1;
playerStrStream1 << "Health:" << std::endl;
Label :: displayString(-0.4 ,0.5, playerStrStream1.str().c_str(),textFont);
std::stringstream playerStrStream2;
playerStrStream2 << "Strength:" << std::endl;
Label :: displayString(-0.4 , -0.2, playerStrStream2.str().c_str(),textFont);
std::stringstream playerStrStream3;
playerStrStream3 << "Speed:" << std::endl;
Label :: displayString(-0.4 , -0.3, playerStrStream3.str().c_str(),textFont);
std::stringstream playerStrStream4;
playerStrStream4 << "Dollars:" << std::endl;
Label :: displayString(-0.4 , -0.4, playerStrStream4.str().c_str(),textFont);
//Enemy
if(enemyNo = 1)
{
Rect * target;
glColor3f(1.0,0.0,0.0);
target = new Rect(0.4f, 0.0f, 0.15f, 0.15f);
target->draw();
std::stringstream targetStrStream1;
targetStrStream1 << "Health:" << std::endl;
Label :: displayString(0.4f,0.5f, targetStrStream1.str().c_str(),textFont);
std::stringstream targetStrStream2;
targetStrStream2 << "Strength:" << std::endl;
Label :: displayString(0.4, -0.2, targetStrStream2.str().c_str(),textFont);
std::stringstream targetStrStream3;
targetStrStream3 << "Speed:" << std::endl;
Label :: displayString(0.4, -0.3, targetStrStream3.str().c_str(),textFont);
std::stringstream targetStrStream4;
targetStrStream4 << "Dollars:" << std::endl;
Label :: displayString(0.4, -0.4, playerStrStream4.str().c_str(),textFont);
}
//brute
if(enemyNo = 2)
{
Rect * brute;
glColor3f(0.6,0.3,0.1);
brute = new Rect(0.6f, 0.0f, 0.15f, 0.15f);
brute->draw();
std::stringstream bruteStrStream1;
bruteStrStream1 << "Health:" << std::endl;
Label :: displayString(0.4, 0.5, bruteStrStream1.str().c_str(),textFont);
std::stringstream bruteStrStream2;
bruteStrStream2 << "Strength:" << std::endl;
Label :: displayString(0.4, -0.2, bruteStrStream2.str().c_str(),textFont);
std::stringstream bruteStrStream3;
bruteStrStream3 << "Speed:" << std::endl;
Label :: displayString(0.4, -0.3, bruteStrStream3.str().c_str(),textFont);
std::stringstream bruteStrStream4;
bruteStrStream4 << "Dollars:" << std::endl;
Label :: displayString(0.4, -0.4, bruteStrStream4.str().c_str(),textFont);
}
//raider
if(enemyNo = 3)
{
Rect * raider;
glColor3f(0.2,0.9,0.4);
raider = new Rect(0.8f, 0.0f, 0.15f, 0.15f);
raider->draw();
std::stringstream raiderStrStream1;
raiderStrStream1 << "Health:" << std::endl;
Label :: displayString(0.4, 0.5, raiderStrStream1.str().c_str(),textFont);
std::stringstream raiderStrStream2;
raiderStrStream2 << "Strength:" << std::endl;
Label :: displayString(0.4, -0.2, raiderStrStream2.str().c_str(),textFont);
std::stringstream raiderStrStream3;
raiderStrStream3 << "Speed:" << std::endl;
Label :: displayString(0.4, -0.3, raiderStrStream3.str().c_str(),textFont);
std::stringstream raiderStrStream4;
raiderStrStream4 << "Dollars:" << std::endl;
Label :: displayString(0.4, -0.4, raiderStrStream4.str().c_str(),textFont);
}
SDL_GL_SwapWindow(window); // swap buffers
}
void StateBattle::handleSDLEvent(SDL_Event const &sdlEvent, Game &context)
{
// Add additional input checks for any other actions carried out in this state
std::cout << "Event handling in main menu state." << std::endl;
if (sdlEvent.type == SDL_KEYDOWN)
{
//std::cout << "Scancode: " << sdlEvent.key.keysym.scancode ;
//std::cout << ", Name: " << SDL_GetKeyName( sdlEvent.key.keysym.sym ) << std::endl;
switch( sdlEvent.key.keysym.sym )
{
case SDLK_RETURN:
context.setState(context.getPlayState());
break;
case SDLK_ESCAPE:
// Create a SDL quit event and push into the event queue
SDL_Event quitEvent;
quitEvent.type = SDL_QUIT;
SDL_PushEvent(&quitEvent);
default:
break;
}
}
}
StateBattle.h
#ifndef STATE_BATTLE
#define STATE_BATTLE
#include "GameState.h"
#include "Label.h"
#include "StatePlay.h"
class StateBattle : public GameState, public StatePlay
{
public:
StateBattle(void);
~StateBattle(void);
void init(Game &context);
void draw(SDL_Window * window);
void handleSDLEvent(SDL_Event const &sdlEvent, Game &context);
void enter();
void exit();
bool fighting;
};
#endif
I've included all the .cpp and .h files involved, hopefully this is enough. If not i can upload more.
Thanks again
You are missing a =, you should write
if(enemyNo == 1)
instead of
if(enemyNo = 1)
But for the next time, please provide a smaller example (as already suggested) with the exact error message.
You should declare the dstor in StatePlay virtual, because it acts as a base class for the StateBattle class. And in the StateBattle implementation file your are initializing enemyNo to 1 instead of comparing it to 1:
//Enemy
if(enemyNo == 1) // <-- missing ==
Inside your states you should only hold state variables: variables that are bound to only this specific state. Enemies could be a state variable if you have just one game state and all other states are complelty independent (i.e. menu state, setting state...). But your states kind of having cross dependencies.
To solve this problem you should extract first everything that doesn't belong to one specific state into another class. I.e. You could have a 'World' class, which manages the list of enemies, terrain, ... Or a class for a List of your Entities. It's a design decision you have to take yourself.
However, your StatePlay shouldn't handle the enemies - and get it inherited by the StateBattle. StateBattle overrides the init(Game &context) function from StatePlay - so it get's never called if you don't call it yourself. The enemyNo in StateBattle is never initalized this way (in fact it is, but just because of another mistake)!
You could write:
void StateBattle::init(Game &context)
{
StatePlay::init(context);
fighting = false;
}
to do so. But like I pointed out, there are some things in your class design, that makes handling these things hard and redundant.
Also you are not checking the enemyNo - you're setting it. = is the assignment Operator - you want to use the compare Operator ==
So better use:
if(enemyNo == 1)
instead of
if(enemyNo = 1)
Otherwise it will set your variable enemyNo to 1 instead of compare it against 1.