#pragma once
#include "Globals.h"
#include "ResourceManager.h"
#include "ResourceIdentifier.h"
#include "Entity.h"
#include <iostream>
#include <cmath>
class Shooter : public Entity{
private:
bool m_isClicked;
bool m_ableToMove;
float m_lastAngle;
bool m_mouseInBounds;
sf::Vector2i m_targetPos;
public:
Shooter();
void initialize(const TextureManager&);
void moveShip(float dt);
void angleShipToMouse(const sf::Vector2i&);
void handleInput(const sf::Event&, const sf::Vector2i&);
void update(const sf::Vector2i&, float);
void adjustingTarget();
};
#include "SpaceShooter.h"
Shooter::Shooter() : m_ableToMove(false), m_isClicked(false), m_lastAngle(0)
{
}
void Shooter::initialize(const TextureManager& text)
{
m_speed = sf::Vector2i(500, 500);
this->m_sprite.setTexture(text.get(TextureID::Shooter));
this->m_sprite.setOrigin(m_sprite.getGlobalBounds().width / 2.0f, m_sprite.getGlobalBounds().height / 2.0f);
this->m_sprite.setPosition(Globals::_windowWidth / 2.0f, Globals::_windowHeight / 2.0f); //start 50 units out
}
void Shooter::moveShip(float dt) {
if (m_isClicked && !m_ableToMove) {
m_ableToMove = true;
m_isClicked = false;
}
if (m_ableToMove) {
adjustingTarget();
sf::Vector2f shipComponentDistances(abs(m_targetPos.x - getX()), abs(m_targetPos.y - getY()));
sf::Vector2f shipVelocity(cos(m_angle * Globals::deg2rad) * dt * m_speed.x,
sin(m_angle*Globals::deg2rad) * dt * m_speed.y);
float targetDistance = sqrt(pow(shipComponentDistances.x, 2) + pow(shipComponentDistances.y, 2));
float distanceToTravel = sqrt(pow(shipVelocity.x, 2) + pow(shipVelocity.y, 2));
if (targetDistance > distanceToTravel) {
this->m_sprite.move(shipVelocity);
m_lastAngle = m_angle;
// std::cout << distance << std::endl;
}
else {
this->m_sprite.setPosition(m_targetPos.x - 0.01, m_targetPos.y - 0.01);
std::cout << m_lastAngle << std::endl;
this->m_sprite.setRotation(m_lastAngle);
std::cout << this->m_sprite.getRotation() << std::endl;
m_ableToMove = false;
m_isClicked = false;
}
}
}
void Shooter::angleShipToMouse(const sf::Vector2i& mousePosition) {
//position of mouse relative to ship in 0 rotation reference frame(absolute)
sf::Vector2f mouseRelativeShip(mousePosition.x - getX(), mousePosition.y - getY());
float rotation = atan2(mouseRelativeShip.y, mouseRelativeShip.x)*Globals::rad2deg; //atan2 produces negative angles if vector is in QUADS 1&2, positive in QUADS 3&4
this->m_sprite.setRotation(rotation);
m_angle = this->m_sprite.getRotation();
}
void Shooter::handleInput(const sf::Event& event, const sf::Vector2i& mousePos) {
if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) {
if (!m_ableToMove && !m_isClicked) {
m_isClicked = true;
m_targetPos = mousePos;
}
}
}
void Shooter::update(const sf::Vector2i& mousePosition, float dt) {
angleShipToMouse(mousePosition);
moveShip(dt);
}
void Shooter::adjustingTarget()
{
if (m_targetPos.x < spriteWidth() / 2.0f) {
m_targetPos.x = spriteWidth() / 2.0f;
}
else if (m_targetPos.x > Globals::_windowWidth - spriteWidth() / 2.0f) {
m_targetPos.x = Globals::_windowWidth - spriteWidth() / 2.0f;
}
else if(m_targetPos.y < spriteHeight() / 2.0f){
m_targetPos.y = spriteHeight() / 2.0f;
}
else if (m_targetPos.y > Globals::_windowHeight - spriteHeight() / 2.0f) {
m_targetPos.y = Globals::_windowHeight - spriteHeight() / 2.0f;
}
}
The problems with my spaceship are:
1. despite not allowing for changing of targetPos when !moving, clicking while its moving to a different location will cause it to redirect the ship. Holding down the ship causes it to follow the mouse cursor.
once ship reaches the targetPosition, the rotation of the ship gets set to 0, im trying to make it retain the rotation of the angle just prior to it reaching its destination (e.g. distance to targetPos is 0).
When it reaches the boundaries sometimes it can go through it despite me adjusting the targetPosition if the mouse is too far into a deadzone the ship cannot enter.
Related
This question already has answers here:
error: no matching function for call to 'begin(int*&)' c++
(3 answers)
Closed last month.
Here is my player class
#pragma once
#include <raylib.h>
#include "SpriteManager.h"
#include "Sprite.h"
#include "Utils.h"
#include "DashAbility.h"
#include "AbilityHolder.h"
class Player : public Sprite
{
public:
float moveSpeed = 300.0f;
DashAbility ability1;
AbilityHolder abilit1Holder;
void init(SpriteManager &spriteManager)
{
texture = spriteManager.player;
x = GetScreenWidth() / 2.0f;
y = GetScreenHeight() / 2.0f;
width = texture.width;
height = texture.height;
ability1.init(3.0f, 1.0f, 1000.0f);
abilit1Holder.init(ability1, KEY_E);
}
void update(Camera2D &camera)
{
vx = heldKeys(KEY_A, KEY_D) * moveSpeed;
vy = heldKeys(KEY_W, KEY_S) * moveSpeed;
if (vx > 0.0f)
fx = 1;
if (vx < 0.0f)
fx = -1;
abilit1Holder.update(this);
x += vx * GetFrameTime();
y += vy * GetFrameTime();
camera.target = (Vector2){x, y};
}
};
And here is my AbilityHolder class
#pragma once
#include <raylib.h>
#include "Ability.h"
class AbilityHolder
{
public:
int key;
float cooldownTimer = 0.0f;
float activeTimer = 0.0f;
bool isCooldown = false;
bool isActive = false;
Ability ability;
void init(Ability _ability, int _key)
{
ability = _ability;
key = _key;
}
void update(Sprite &target)
{
if (IsKeyPressed(key) && !isCooldown && !isActive)
{
isActive = true;
}
if (isActive)
{
ability.active(target);
activeTimer += GetFrameTime();
if (activeTimer > ability.activeTime)
{
isActive = false;
isCooldown = true;
activeTimer = 0.0f;
}
}
if (isCooldown)
{
cooldownTimer += GetFrameTime();
if (cooldownTimer > ability.cooldownTime)
{
isCooldown = false;
cooldownTimer = 0.0f;
}
}
}
};
Inside update function of Player class, I want to call ability1Holder update function and it takes 1 argument, and I put "this" inside it. But code blocks gives me this error:
include\Player.h|41|error: no matching function for call to
'AbilityHolder::update(Player*)'|
Try changing abilit1Holder.update(this); to abilit1Holder.update(*this);.
I decided to make a multiplayer game in sfml, the map is a text variable
one step lower,! - block with a collider, # - ground.
It looks something like this:
"## !;
! # !;
###;"
I have a special class "Qardrat" that represents a block with a texture, that is, an alternative to a sprite.
class Quardrat {
public:
void spriteSetPosition(int Vx, int Vy) {
sprite.setPosition(Vx, Vy);
}
void LoadTexture(String textureName) {
texture.loadFromFile(textureName);
// RectangleShape tmpSprite(texture);
sprite.setScale(sizeX, sizeY);
sprite.setTexture(&texture);
}
void ShapeMove(int Vx, int Vy) {
sprite.move(Vx, Vy);
//sprite.setPosition(sprite.getPosition().x+Vx, sprite.getPosition().y+Vy);
std::cout << "Сдвинулся на " << Vx << "По x" << std::endl;
}
Quardrat(float x = 0, float y = 0, float sx = 1, float sy = 1, String textureName = "player.png") {
LoadTexture(textureName);
sizeX = sx;
sizeY = sy;
sprite.setPosition(x, y);
sprite.setSize(Vector2f(sx, sy));
}
sf::RectangleShape GetShape() {
return sprite;
}
void DrawShape() {
::window.draw(sprite);
}
float GetSizeX() {
return sizeX;
}float GetSizeY() {
return sizeY;
}
private:
Texture texture;
std::string texutreName = "player.png";
float sizeX = 10;
float sizeY = 10;
//Sprite sprite;
RectangleShape sprite;
};
It is declared as follows: x position, y position, height, width, texture name.
To draw objects, 3 cities are used, which are placed in the square classes:
sloi0, sloi1, player /
here is the drawing code
void DrawOnDisplay(const std::vector<std::reference_wrapper<Quardrat>>& a) {//РИСОВАНИЕ
for (Quardrat i : sloi0)
i.DrawShape();
//::window.draw(i.GetShape());
for (Quardrat i : sloi1)
i.DrawShape();
// ::window.draw(i.GetShape());
for (Quardrat i : a) {
i.DrawShape();
// ::window.draw(i.GetShape());
}
}
And I'll leave the card reading code here, just in case:
for (char block : map) {
if (block == '#') {
Quardrat bl = Quardrat(BlockPosX * StandartBlockSize, BlockPosY * StandartBlockSize, StandartBlockSize, StandartBlockSize);
sloi0.push_back(bl); BlockPosX++;
// }
if (block == '!') {
Quardrat bl = Quardrat(BlockPosX * StandartBlockSize, BlockPosY * StandartBlockSize,
StandartBlockSize / 10, StandartBlockSize / 10, "block.png");
sloi0.push_back(bl); BlockPosX++;
collisions.push_back(bl);
}
if (block == ';') {
BlockPosY++;
BlockPosX = 0;
}
}
And here's the problem - from all the blocks of the map, a texture appears only for one, for all the rest - a white square.
UPDATE
I see, providing code partly doesn't decide the problem. There are a lot of files, so I'll provide all aplication via GitHub for anybody would like to try to decide this problem:
https://github.com/johnydominus/CrimsonLikeGameSMFL
Sorry in advance for my English and programming knowledge. I'm newbie.
I'm making a 2D game using C++ and SFML SDK. The game is similar to CrimsonLand (2003): player should be walking on map and shooting monsters, while they trying to reach him.
For the moment player is walking and monsters are chasing him, but only if their direction is left or up. If needed direction is right or down - they don't move. Monsters just staying and staring at players direction. And player just doesn't move when right or down buttons pressed.
I've implement 2 movement coordinates systems - relative to map (to handle game events, like intersection monsters with bullets) and relative to player (to center "camera" on player). Movement written to map coordinates first, then it is transformed to player relative coordinates. Hence, drawing is using player relative coordinates. However, it doesn't look like that problem is in drawing.
Input is working - I've tried to change movement assignment (just for check) and set to move left when right button pressed and up when down button pressed - and it worked: player moved up both by up and down buttons and moved left by left and right buttons.
I'll try to delete all strings, that don't relate to the problem. But due to fact, that I don't have a clear idea what is wrong - there will be quite a lot of code.
Map, monster and player headers, and .cpp files - declaration and definition of game objects.
Engine header and .cpp - declaration and definition of engine, that handles objects interaction.
Input and update .cpp's - definition of Engine methods, that handle respectively input from keyboard and updating objects position and state.
player.h
class Player :
public Object
{
private:
std::vector<float> mapSize{0,0};
int speed = 1;
POINT prevPosition;
std::vector<float> relatMovement{ 0,0 };
bool leftPressed;
bool rightPressed;
bool upPressed;
bool downPressed;
public:
POINT Position;
POINT relatPosition;
void moveLeft();
void moveRight();
void moveUp();
void moveDown();
void stopLeft();
void stopRight();
void stopUp();
void stopDown();
void update(float elapsedTime);
};
player.cpp
void Player::moveLeft()
{
leftPressed = true;
}
void Player::moveRight()
{
rightPressed = true;
}
void Player::moveUp()
{
upPressed = true;
}
void Player::moveDown()
{
downPressed = true;
}
void Player::stopLeft()
{
leftPressed = false;
}
void Player::stopRight()
{
rightPressed = false;
}
void Player::stopUp()
{
upPressed = false;
}
void Player::stopDown()
{
downPressed = false;
}
void Player::update(float elapsedTime)
{
if (rightPressed)
Position.x += speed * elapsedTime;
if (leftPressed)
Position.x -= speed * elapsedTime;
if (upPressed)
Position.y -= speed * elapsedTime;
if (downPressed)
Position.y += speed * elapsedTime;
relatMovement[0] = Position.x - prevPosition.x;
relatMovement[1] = Position.y - prevPosition.y;
prevPosition = Position;
}
monster.h
class Monster :
public Object
{
private:
float pathLength;
Player* thePlayer;
Map* theMap;
POINT playerPosition;
POINT playerRelatPosition;
POINT nextStep;
std::vector<float> playerRelatMovement{0,0};
std::vector<float> direction{ 0,0 };
std::vector<float> vSpeed{ 0,0 };
public:
POINT Position;
POINT relatPosition;
POINT checkUpdate(float elapsedTime);
void update(float elapsedTime, POINT position);
};
monster.cpp
POINT Monster::checkUpdate(float elapsedTime)
{
nextStep = Position;
playerPosition = *(thePlayer->getPosition());
direction[0] = playerPosition.x - Position.x;
direction[1] = playerPosition.y - Position.y;
pathLength = sqrt(pow(direction[0], 2) + pow(direction[1], 2));
direction[0] /= pathLength;
direction[1] /= pathLength;
vSpeed[0] = ((float)direction[0] * (float)speed)/10.0;
vSpeed[1] = ((float)direction[1] * (float)speed)/10.0;
nextStep.x += vSpeed[0];
nextStep.y += vSpeed[1];
return nextStep;
}
void Monster::update(float elapsedTime, POINT aNextStep)
{
Position = aNextStep;
playerPosition = *(thePlayer->getPosition());
playerRelatPosition = *(thePlayer->getRelatPosition());
relatPosition.x = playerRelatPosition.x + (Position.x - playerPosition.x);
relatPosition.y = playerRelatPosition.y + (Position.y - playerPosition.y);
shape.left = Position.x - (size[0] / 2);
shape.right = Position.x + (size[0] / 2);
shape.top = Position.y - (size[1] / 2);
shape.bottom = Position.y + (size[1] / 2);
}
map.h
class Map:
public Object
{
private:
std::vector<float> relatMovement{0,0};
std::vector<float> size{0,0};
Player* thePlayer;
public:
POINT Position;
POINT relatPosition;
void update();
};
map.cpp
Map::Map()
{
Position.x = 0;
Position.y = 0;
}
void Map::update()
{
relatMovement = *(thePlayer->getRelatMovement());
relatPosition.x -= relatMovement[0];
relatPosition.y -= relatMovement[1];
}
Engine.h
class Engine
{
private:
Player thePlayer;
Map theMap;
Monster* allMonsters;
int mapXstart=0, mapYstart=0, ammoNumberStart=0, enemiesNumberStart=0;
void input();
void update(float timeInSeconds);
void draw();
void setWindowSize(int mapX, int mapY);
void setMapSize(float mapWidth, float mapHeight);
public:
void start();
};
Engine.cpp
Engine::Engine()
{
//setting map sprites
a = ((mapY+windowY) / theMap.mSprite.getTexture()->getSize().y) + 1;
b = ((mapX+windowX) / theMap.mSprite.getTexture()->getSize().x) + 1;
mapSprites = new sf::Sprite*[a];
for (i = 0; i < a; i++) {
mapSprites[i] = new sf::Sprite[b];
for (j = 0; j < b; j++) {
mapSprites[i][j].setTexture(*theMap.mSprite.getTexture());
}
}
//setting window
mWindow.create(sf::VideoMode(windowX, windowY), "CrimsonLikeGame", sf::Style::Default);
//setting game objects
//map
int mapRelX, mapRelY;
mapRelX = (windowX / 2) - (mapX / 2);
mapRelY = (windowY / 2) - (mapY / 2);
theMap.setRelativePosition(mapRelX, mapRelY);
theMap.setSize(mapX, mapY);
theMap.setPlayer(&thePlayer);
//player
thePlayer.setPosition(mapX/2,mapY/2);
thePlayer.setRelativePosition(windowX / 2, windowY / 2);
thePlayer.setMapSize(mapX, mapY);
//monsters
allMonsters = new Monster[enemiesNumber];
for (i = 0; i < enemiesNumber; i++) {
allMonsters[i].setPlayer(&thePlayer);
allMonsters[i].setMap(&theMap);
}
}
void Engine::start()
{
sf::Clock clock;
//game loop
while (mWindow.isOpen()) {
sf::Time dt = clock.restart();
float dtAsSeconds = dt.asSeconds();
input();
update(dtAsSeconds);
draw();
}
}
input.cpp
void Engine::input() {
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) {
mWindow.close();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
thePlayer.moveLeft();
}
else {
thePlayer.stopLeft();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
thePlayer.moveRight();
}
else {
thePlayer.stopRight();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
thePlayer.moveUp();
}
else {
thePlayer.stopUp();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
thePlayer.moveDown();
}
else {
thePlayer.stopDown();
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
mouseButtonPressed = true;
}
else {
mouseButtonPressed = false;
}
}
update.cpp
void Engine::update(float timeInSeconds) {
if (thePlayer.isAlive()&&enemiesAlive) {
thePlayer.update(timeInSeconds);
theMap.update();
//Writing down, where each monster is going to go by it's next step
for (i = 0; i < enemiesNumber; i++) {
if (allMonsters[i].isAlive()) {
enemiesNextSteps[i] = allMonsters[i].checkUpdate(timeInSeconds);
}
}
//cheking - does anybody is going to collide
for (i = 0; i < enemiesNumber; i++) {
if (allMonsters[i].isAlive()) {
int j;
for (j = 0; j < enemiesNumber; j++) {
if (j == i)
continue;
else {
if ((((allMonsters[i].shape.left <= allMonsters[j].shape.right) && (allMonsters[i].shape.left >= allMonsters[j].shape.left)) || ((allMonsters[i].shape.right <= allMonsters[j].shape.right) && (allMonsters[i].shape.right >= allMonsters[j].shape.left))) && (((allMonsters[i].shape.bottom >= allMonsters[j].shape.top) && (allMonsters[i].shape.bottom <= allMonsters[j].shape.bottom)) || ((allMonsters[i].shape.top >= allMonsters[j].shape.top) && (allMonsters[i].shape.top <= allMonsters[j].shape.bottom)))) {
monstersCollide[i] = true;
}
}
}
}
}
//updating each alive monster position without collisions
for (i = 0; i < enemiesNumber; i++) {
if (allMonsters[i].isAlive()/*&&!monstersCollide[i]*/) {
allMonsters[i].setPosition(enemiesNextSteps[i]);
allMonsters[i].update(timeInSeconds, enemiesNextSteps[i]);
}
}
}
else {
//if player is dead - restart the game
thePlayer.setAlive(true);
for (i = 0; i < enemiesNumber; i++) {
allMonsters[i].setAlive(true);
}
}
}
I was trying to figure it out half a day. Hope you can help me with that problem.
Okay. I've actually built and ran this code, and the root cause for your movement problem is using integers for your coordinates, and ensuing assymetry:
if (rightPressed)
Position.x += speed * elapsedTime;
if (leftPressed)
Position.x -= speed * elapsedTime;
Those two seem pretty equal at first, but when you consider what really happens, they differ slightly. Because your speed is relatively low (1.0), and so is your elapsed time (e.g. ~0.016 for one frame), the differences end up being less by one. To understand what happens next, you need to look at type conversions.
The statements are actually equivalent to:
Position.x = Position.x + (speed * elapsedTime);
Because speed and elapsedTime are floating point numbers, Position.x gets promoted to a float as well. Then the fraction is either added and subtracted, and then the result is converted back to an integer.
In case of moving left, the number e.g. 100 is converted to 100.0, then 0.016 gets subtracted, resulting in 99.984. Then, integer conversion removes the decimal part, resulting in the value of 99 and an observable change in your player's position.
In the case of moving right, we do the same, but we end up with a value of 100.016 instead. This converted back to integer results with a value of 100 again.
To fix this, the easiest solution is to make the player's position use floats as well. Then the small changes will be properly accumulated. You might observe that the player moves way slower than you'd expect at first, because the integer clamping effect will dissappear; setting the speed to 60 should get you more or less back where it was.
When I try to compile the following code:
Alien::Action::Action(ActionType type, float x, float y) {
Action::type = type;
pos = Vec2(x, y);
}
Alien::Alien(float x, float y, int nMinions) {
srand(time(NULL));
sp = Sprite("img/alien.png");
box = Rect(x, y, sp.GetWidth(), sp.GetHeight());
box.x = x - box.h/2;
box.y = y - box.w/2;
hitpoints = 100;
speed.x = 0.5;
speed.y = 0.5;
minionArray = std::vector<Minion>();
for(int i = 0; i < nMinions; i++) {
int a = rand()%501;
float b = a/1000.0;
float c = b+1;
minionArray.emplace_back(Minion(get(), i*(360/nMinions), c));
}
taskQueue = std::queue<Action>();
}
The IDE (Eclipse) gives the following error message: "undefined reference to 'vtable for Alien'" (line 6 of the code). Since there's no virtual method inside Alien, I don't know the cause of the error. The following is the header file for Alien:
#ifndef ALIEN_H_
#define ALIEN_H_
#include "GameObject.h"
class Alien: public GameObject {
private:
class Action {
public:
enum ActionType {MOVE, SHOOT};
ActionType type;
Action(ActionType type, float x, float y);
Vec2 pos;
};
int hitpoints;
std::queue<Action> taskQueue;
std::vector<Minion> minionArray;
public:
Alien(float x, float y, int nMinions);
~Alien();
void Update(float dt);
void Render();
Alien* get();
bool IsDead();
};
#endif
The code for GameObject is :
#include "GameObject.h"
#include "InputManager.h"
#include "Camera.h"
#include "State.h"
GameObject::~GameObject() {
}
GameObject* GameObject::get() {
return this;
}
Minion::~Minion() {
}
Minion::Minion(GameObject* minionCenter, float arcOffset, float minionSize) {
sp = Sprite("img/minion.png");
center = minionCenter;
translation = arcOffset;
box = Rect(center->box.GetCenter().x+(cos(translation*M_PI/180)*200)-(sp.GetWidth()/2),
center->box.GetCenter().y+(sin(translation*M_PI/180)*200)-(sp.GetHeight()/2),
sp.GetWidth(), sp.GetHeight());
}
void Minion::Shoot(Vec2 pos) {
State::AddObject(new BulletWheel(box.GetCenter().x, box.GetCenter().y, center->box.GetCenter().GetDX(pos.x),
center->box.GetCenter().GetDY(pos.y), center->box.GetCenter().GetDS(pos), 0.3,
translation, center->box.GetCenter(), "img/minionbullet1.png"));
}
void Minion::Update(float dt) {
if(translation < 360)
translation += 0.03*dt;
else
translation += 0.03*dt-360;
/*rotation = translation-90;*/
if(rotation < 360)
rotation += 0.15*dt;
else
rotation += 0.15*dt-360;
box.x = center->box.GetCenter().x+(200*cos((translation)*M_PI/180))-(box.w/2);
box.y = center->box.GetCenter().y+(200*sin((translation)*M_PI/180))-(box.h/2);
}
void Minion::Render() {
sp.Render(box.x - Camera::GetInstance().pos.x, box.y - Camera::GetInstance().pos.y, rotation);
}
bool Minion::IsDead() {
return false;
}
Bullet::Bullet(float x, float y, float dx, float dy, float maxDistance, float speed, std::string sprite) {
sp = Sprite(sprite);
box = Rect(x-(sp.GetWidth()/2), y-(sp.GetHeight()/2), sp.GetWidth(), sp.GetHeight());
Bullet::speed = Vec2(speed*(dx/maxDistance), speed*(dy/maxDistance));
distanceLeft = maxDistance;
rotation = atan2(dy, dx)*(180/M_PI);
}
void Bullet::Update(float dt) {
if(distanceLeft > 0) {
box.x += speed.x*dt;
box.y += speed.y*dt;
distanceLeft -= pow(pow(speed.x*dt,2)+pow(speed.y*dt,2),0.5);
}
}
void Bullet::Render() {
sp.Render(box.x - Camera::GetInstance().pos.x, box.y - Camera::GetInstance().pos.y, rotation);
}
bool Bullet::IsDead() {
return (distanceLeft < 1) ? true : false;
}
Bullet* Bullet::get() {
return this;
}
BulletWheel::BulletWheel(float x, float y, float dx, float dy, float maxDistance, float speed, float arcOffset, Vec2 center, std::string sprite) {
sp = Sprite(sprite);
sp.SetScaleX(2);
sp.SetScaleY(2);
box = Rect(x-(sp.GetWidth()/2), y-(sp.GetHeight()/2), sp.GetWidth(), sp.GetHeight());
BulletWheel::speed = Vec2(speed*(dx/maxDistance), speed*(dy/maxDistance));
distanceLeft = maxDistance;
rotation = atan2(dy, dx)*(180/M_PI);
translation = arcOffset;
BulletWheel::center = center;
}
void BulletWheel::Update(float dt) {
if(translation < 360)
translation += 0.1*dt;
else
translation += 0.1*dt-360;
if(distanceLeft > 0.01) {
center.x += speed.x*dt;
center.y += speed.y*dt;
box.x = center.x+(200*cos((translation)*M_PI/180))-(box.w/2);
box.y = center.y+(200*sin((translation)*M_PI/180))-(box.h/2);
distanceLeft -= pow(pow(speed.x*dt,2)+pow(speed.y*dt,2),0.5);
}
}
void BulletWheel::Render() {
sp.Render(box.x - Camera::GetInstance().pos.x, box.y - Camera::GetInstance().pos.y, rotation);
}
bool BulletWheel::IsDead() {
return distanceLeft < 1;
}
BulletWheel* BulletWheel::get() {
return this;
}
and its header file is:
#ifndef GAMEOBJECT_H_
#define GAMEOBJECT_H_
#include "Sprite.h"
#include "Rect.h"
#include "Vec2.h"
#include <queue>
#include <vector>
#include <cmath>
#include <ctime>
class GameObject{
private:
public:
virtual ~GameObject();
virtual void Update(float dt) = 0;
virtual void Render() = 0;
virtual bool IsDead() = 0;
virtual GameObject* get();
int rotation = 0;
int translation = 0;
Sprite sp = Sprite();
Vec2 speed = Vec2();
Rect box = Rect();
};
class Minion : public GameObject {
private:
GameObject* center;
public:
Minion(GameObject* minionCenter, float arcOffset, float minionSize = 1);
~Minion();
void Shoot(Vec2 pos);
void Update(float dt);
void Render();
bool IsDead();
Minion* get();
};
class Bullet : public GameObject {
private:
float distanceLeft;
public:
Bullet(float x, float y, float dx, float dy, float maxDistance, float speed, std::string sprite);
void Update(float dt);
void Render();
bool IsDead();
Bullet* get();
};
class BulletWheel : public GameObject {
private:
float distanceLeft;
Vec2 center;
public:
BulletWheel(float x, float y, float dx, float dy, float maxDistance, float speed, float arcOffset, Vec2 center, std::string sprite);
void Update(float dt);
void Render();
bool IsDead();
BulletWheel* get();
};
#endif /* GAMEOBJECT_H_ */
There are the virtual functions of GameObject, declared inside Alien.cpp:
void Alien::Update(float dt) {
if(rotation > 0)
rotation -= 0.1*dt;
else
rotation -= 0.1*dt+360;
if(InputManager::GetInstance().MousePress(RIGHT_MOUSE_BUTTON)) {
taskQueue.push(Action(Action::MOVE,(InputManager::GetInstance().GetMouseX() + Camera::GetInstance().pos.x - (box.w/2)),
(InputManager::GetInstance().GetMouseY() + Camera::GetInstance().pos.y - (box.h/2))));
}
if(InputManager::GetInstance().MousePress(LEFT_MOUSE_BUTTON)) {
taskQueue.push(Action(Action::SHOOT,(InputManager::GetInstance().GetMouseX() + Camera::GetInstance().pos.x),
(InputManager::GetInstance().GetMouseY() + Camera::GetInstance().pos.y)));
}
if(taskQueue.size() > 0) {
Vec2 pos = taskQueue.front().pos;
if(taskQueue.front().type == Action::MOVE) {
float cos = (box.GetDX(pos.x)/box.GetDS(pos));
float sin = (box.GetDY(pos.y)/box.GetDS(pos));
if(cos != cos) {
cos = 0;
}
if(sin != sin) {
sin = 0;
}
if((box.x+speed.x*cos*dt > pos.x && pos.x > box.x) || (box.x+speed.x*cos*dt < pos.x && pos.x < box.x)) {
box.x = pos.x;
}
else {
box.x += speed.x*cos*dt;
}
if((box.y+speed.y*sin*dt > pos.y && pos.y > box.y) || (box.y+speed.y*sin*dt < pos.y && pos.y < box.y)) {
box.y = pos.y;
}
else {
box.y += speed.y*sin*dt;
}
if(box.x == pos.x && box.y == pos.y) {
taskQueue.pop();
}
}
else {
for(unsigned i = 0; i < minionArray.size(); i++) {
minionArray.at(i).Shoot(pos);
taskQueue.pop();
}
}
}
for(unsigned i = 0; i < minionArray.size(); i++) {
minionArray.at(i).Update(dt);
}
}
void Alien::Render() {
sp.Render(box.x - Camera::GetInstance().pos.x, box.y - Camera::GetInstance().pos.y, rotation);
if(minionArray.size() > 0) {
for(unsigned i = 0; i < Alien::minionArray.size(); i++) {
minionArray.at(i).Render();
}
}
}
bool Alien::IsDead() {
return (Alien::hitpoints <= 0);
}
EDIT: the destructor of Alien was missing.
All classes derived from GameObject must define all pure virtual functions in GameObject. In your case, this is:
virtual void Update(float dt) = 0;
virtual void Render() = 0;
virtual bool IsDead() = 0;
Here is a similar question with more information. Hope this helps!
I am working with some code to create a triangle that moves with arrow keys. I want to create a second object that moves independently. This is where I am having trouble, I have created the second actor, but cannot get it to move. There is too much code to post it all so I will just post a little and see if anyone can help at all.
ogl_test.cpp
#include "platform.h"
#include "srt/scheduler.h"
#include "model.h"
#include "controller.h"
#include "model_module.h"
#include "graphics_module.h"
class blob : public actor {
public:
blob(float x, float y) : actor(math::vector2f(x, y)) { }
void render() {
transform();
glBegin(GL_TRIANGLES);
glVertex3f(0.25f, 0.0f, -5.0f);
glVertex3f(-.5f, 0.25f, -5.0f);
glVertex3f(-.5f, -0.25f, -5.0f);
glEnd();
end_transform();
}
void update(controller& c, float dt) {
if (c.left_key) {
rho += pi / 9.0f * dt;
c.left_key = false;
}
if (c.right_key) {
rho -= pi / 9.0f * dt;
c.right_key = false;
}
if (c.up_key) {
v += .1f * dt;
c.up_key = false;
}
if (c.down_key) {
v -= .1f * dt;
if (v < 0.0) { v = 0.0; }
c.down_key = false;
}
actor::update(c, dt);
}
};
class enemyOne : public actor {
public:
enemyOne(float x, float y) : actor(math::vector2f(x, y)) { }
void render() {
transform();
glBegin(GL_TRIANGLES);
glVertex3f(0.25f, 0.0f, -5.0f);
glVertex3f(-.5f, 0.25f, -5.0f);
glVertex3f(-.5f, -0.25f, -5.0f);
glEnd();
end_transform();
}
void update(controller& c, float dt) {
if (c.left_key) {
rho += pi / 9.0f * dt;
c.left_key = false;
}
if (c.right_key) {
rho -= pi / 9.0f * dt;
c.right_key = false;
}
if (c.up_key) {
v += .1f * dt;
c.up_key = false;
}
if (c.down_key) {
v -= .1f * dt;
if (v < 0.0) { v = 0.0; }
c.down_key = false;
}
actor::update(c, dt);
}
};
int APIENTRY WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
char* lpCmdLine,
int nCmdShow
)
{
model m;
controller control(m);
srt::scheduler scheduler(33);
srt::frame* model_frame = new srt::frame(scheduler.timer(), 0, 1, 2);
srt::frame* render_frame = new srt::frame(scheduler.timer(), 1, 1, 2);
model_frame->add(new model_module(m, control));
render_frame->add(new graphics_module(m));
scheduler.add(model_frame);
scheduler.add(render_frame);
blob* prime = new blob(0.0f, 0.0f);
m.add(prime);
m.set_prime(prime);
enemyOne* primeTwo = new enemyOne(2.0f, 0.0f);
m.add(primeTwo);
m.set_prime(primeTwo);
scheduler.start();
control.start();
return 0;
}
model.h
#include <vector>
#include "vec.h"
const double pi = 3.14159265358979323;
class controller;
using math::vector2f;
class actor {
public:
vector2f P;
float theta;
float v;
float rho;
actor(const vector2f& init_location) :
P(init_location),
rho(0.0),
v(0.0),
theta(0.0)
{ }
virtual void render() = 0;
virtual void update(controller&, float dt) {
float v1 = v;
float theta1 = theta + rho * dt;
vector2f P1 = P + v1 * vector2f(cos(theta1), sin(theta1));
if (P1.x < -4.5f || P1.x > 4.5f) { P1.x = -P1.x; }
if (P1.y < -4.5f || P1.y > 4.5f) { P1.y = -P1.y; }
v = v1;
theta = theta1;
P = P1;
}
protected:
void transform() {
glPushMatrix();
glTranslatef(P.x, P.y, 0.0f);
glRotatef(theta * 180.0f / pi, 0.0f, 0.0f, 1.0f); //Rotate about the z-axis
}
void end_transform() {
glPopMatrix();
}
};
class model {
private:
typedef std::vector<actor*> actor_vector;
actor_vector actors;
public:
actor* _prime;
model() { }
void add(actor* a) {
actors.push_back(a);
}
void set_prime(actor* a) {
_prime = a;
}
void update(controller& control, float dt) {
for (actor_vector::iterator i = actors.begin(); i != actors.end(); ++i) {
(*i)->update(control, dt);
}
}
void render() {
for (actor_vector::iterator i = actors.begin(); i != actors.end(); ++i) {
(*i)->render();
}
}
};
Your blob is erasing the keypresses before the second actor sees them:
if (c.left_key) {
rho += pi / 9.0f * dt;
c.left_key = false; // <-- HERE
}
Clearing the keyboard state after each frame should not be the concern of any single actor, the controller itself should do that.
Both blob and enemyOne both have an update function that checks the arrow keys to see whether to move. If you commented out all of the if statements inside one of the update function then it should not move anymore. Alternatively, you could change the if statements in the update function to check other keys and use those to control the other object.
EDIT: As noted in another answer, when you set c.left_key = false; (or one on the other keys) you prevent any of the other actors from seeing that the key was pressed. Either not passing the controller around as a reference, or removing these lines of code should fix this.