My instantiations stop after a few spawns, but I want them to keep spawning forever thru the game - destroy

My instantiations stop after 1 or 2 spawns, but I want them to keep spawning forever thru the game.It's because I have the destroy piece of code in this script, but what can I do I need them to die after collision or there will be hundreds on screen like before. As usual when dead an enemy must die and disappear. So what do I do? The destroy code is destroying the entire enemy player /prefab not just the dead instance like I want.
I want the enemies to keep spawning but of course when killed by player they must disappear.
2d game I have this script on the enemy prefab and in scene enemies
I want the enemy on screen to die one by one when collision but the destroy is killing the entire object / prefab I think and it will not instantiate /spawn any more
I do have a few scripts on the enemy here is the instantiation script
i have used a off-screen instance (tried) but these enemies have moving scripts on them ..i put a off-screen object like you said but all the objects have a moving script since the enemy's point is to fly thru the screen so player tries to kill as many enemies as he can so if I take off the moving script they do spawn constantly but when I put the move script on they die and do not spawn, and again I need them to move thru the screen, so im stuck I still cant figure this one out
public class spn2 : MonoBehaviour {
GameObject Enemy;
//public GameObject EasyEnemey;
public GameObject MediumEnemey;
public GameObject HardEnemey;
public Transform[] SpawnPoints;
public float TimeBetweenSpawns;
public int NumberOfEnemiesToSpawn;
public int NumberOfMediumEnemiesToSpawn;
public float EasyChance;
public float MediumChance;
public float HardChance;
private int waveNumber;
private float spawnTimer;
private int numberOfEnemies;
private int numberOfMediumEnemies;
// Use this for initialization
void Start()
{
//this below is the time to spawn so if 4 , every 4 seconds 1 will spawn etc
this.spawnTimer = 3.0f;
this.waveNumber = 0;
float totalChance = this.EasyChance + this.MediumChance + this.HardChance;
if(Mathf.Abs(totalChance-1.0f)>0.0001f) {
Debug.LogWarning("Warning: The chances should add up to 1.0 ("+totalChance+" currently)");
}
}
// Update is called once per frame
void Update()
{
this.spawnTimer -= Time.deltaTime;
if(this.spawnTimer<=0.0f)
{
Transform spawnPoint = this.SpawnPoints[Random.Range(0, this.SpawnPoints.Length)];
Vector2 spawnPos = spawnPoint.position;
Quaternion spawnRot = spawnPoint.rotation;
switch(this.waveNumber)
{
case 0:
//Instantiate(EasyEnemey, spawnPos,spawnRot);
Instantiate(Resources.Load(Enemy) as GameObject, spawnPos, spawnRot);
this.numberOfEnemies++;
if(this.numberOfEnemies>=this.NumberOfEnemiesToSpawn)
{
this.waveNumber++;
}
break;
case 1:
Instantiate(MediumEnemey, spawnPos, spawnRot);
this.numberOfMediumEnemies++;
if (this.numberOfMediumEnemies >= this.NumberOfMediumEnemiesToSpawn)
{
this.waveNumber++;
}
break;
case 2:
float randomFloat = Random.value;
if(randomFloat<this.EasyChance)
{
Instantiate(Enemy, spawnPos, spawnRot);
}
else if(randomFloat<this.EasyChance+this.MediumChance)
{
Instantiate(MediumEnemey, spawnPos, spawnRot);
}
else
{
Instantiate(HardEnemey, spawnPos, spawnRot);
}
break;
}
this.spawnTimer = this.TimeBetweenSpawns;
Destroy (gameObject, .7f);
}
}
}

Related

C++: Instantiate same object as one of many types depending on variable

I am programming an LED Cube I have designed. The cube has a "pause" button and a "play/next" button. Unless the cube is paused, it will cycle through all of the different effects (animations) I've made for it. If you press the pause button, the cube will no longer transition between effects and will instead repeat the current effect. Pressing the 'play/next' button will unset the pause feature and will advance to the next effect immediately.
Some of these effects are pretty complex and require a large number of variables to be kept between frames of animation. In order to easily destroy all of these variables at a moment's notice (like when the next button is pressed), I'm instantiating the current animation as an object and destroying it when the effect is complete or the skip button is pressed.
I'm trying to set my main loop up as follows:
void loop() {
//create an effect object
switch(effectIndex){
case 0:
EF_GROWFRAME effect;
break;
case 1:
EF_RANDOMFILL effect;
break;
}
bool proceed;
do{
//returns false until the effect has completed
proceed=effect.step();
//push this cube update and wait for it to display
cube.update();
cube.waitForFrame();
}
while ((!proceed)&&(!skipflag));
//skipflag is set true during a timer interrupt if the skip button is freshly pressed
skipflag=false;
cube.clearPattern();
if (play) effectIndex++;
if (effectIndex=effectCount) effectIndex=0;
}
That fails because of my conflicting definitions of effect though. You can probably see what I'm going for, so what's the proper way to approach this?
This is a use case for polymorphism.
Define a base class, Animation that defines a shared interface and have your various animation types derive from it. For example:
class Animation {
public:
virtual ~Animation() {
// any generic cleanup shared by all animation types
}
virtual bool step() = 0;
};
class AnimationA : public Animation {
public:
bool step() override {
// logic for this type of animation
}
};
class AnimationB : public Animation {
public:
bool step() override {
// logic for this type of animation
}
};
void loop() {
std::unique_ptr<Animation> effect;
switch (effectIndex) {
case 0:
effect = std::make_unique<AnimationA>();
break;
case 1:
effect = std::make_unique<AnimationB>();
break;
}
//...
}
Live Demo
Since it seems like this may be an embedded environment, you could avoid the dynamic memory allocation from my first example by factoring your animation playing logic out into a separate function:
void playAnimation(Animation& effect) {
bool proceed;
do{
//returns false until the effect has completed
proceed=effect.step();
//push this cube update and wait for it to display
cube.update();
cube.waitForFrame();
} while (!proceed && !skipFlag);
//skipflag is set true during a timer interrupt if the skip button is freshly pressed
skipflag=false;
cube.clearPattern();
}
void loop() {
switch (effectIndex) {
case 0:
{
AnimationA effect;
playAnimation(effect);
break;
}
case 1:
{
AnimationB effect;
playAnimation(effect);
break;
}
}
if (play) effectIndex++;
if (effectIndex == effectCount) effectIndex=0;
}
Live Demo

SFML objects won't draw when its parent class is reinitialised

I'm working on a new project and an implementing a basic scene change. I have the different scenes setup as their own classes, with the intialisation function being used to create and reposition different SFML objects. I saw this answer and have written my scene switcher similarly:
// Create scene monitoring variable
int scene[2];
scene[0] = 0; // Set current scene to menu
scene[1] = 0; // Set scene change to no
...
// Check for scene change
if(scene[1] == 0) {
// Run tick function based on current scene
switch(scene[0]) {
case 0:
// Main menu - run tick function
menu.tick();
}
}
if(scene[1] == 1) {
// Reset scene that you've changed to
switch(scene[0]) {
case 0:
// Main menu - reset it
menu = Menu(window, scene); // <-- Reinitialise menu here
}
// Set change variable to 0
scene[1] = 0;
}
You can see the full code on the github repository.
However, this doesn't seem to work properly - as soon as a scene change is made, the screen goes blank. The class is reintialised (I added a cout to check), the draw function is still run and mouse clicks are still processed, yet nothing appears in the window.
Am I doing something wrong here?
Doing things that way can lead into leak memory errors. I suggest you a different approach: the StateStack
How this works?
The basics of having a StateStack object is store each possible state of your game/app into a stack. This way, you can process each one in the stack order.
What is an State?
An State is something that can be updated, drawn and handle events. We can make an interface or an abstract class to make our screens behave like a State.
Which are the advantages?
With a stack structure, you can easily control how your different scenes are going to handle the three different processing methods. For instance. If you have a mouse click while you're in a pause menu, you won't that click event to reach the menu state or the "game" state. To achieve this, the solution is really easy, simply return false in your handleEvent method if you don't want the event go further this particular state. Note that this idea is also expandable to draw or update methods. In your pause menu, you won't update your "game" state. In your "game" state you won't draw tour menu state.
Example
With this points in mind, this is one possible way of implementation. First, the State interface:
class State{
public:
virtual bool update() = 0;
virtual bool draw(sf::RenderTarget& target) const = 0;
// We will use a vector instead a stack because we can iterate vectors (for drawing, update, etc)
virtual bool handleEvent(sf::Event e, std::vector<State*> &stack) = 0;
};
Following this interface we can have a example MenuState and PauseState:
MenuState
class MenuState : public State{
public:
MenuState(){
m_count = 0;
m_font.loadFromFile("Roboto-Regular.ttf");
m_text.setFont(m_font);
m_text.setString("MenuState: " + std::to_string(m_count));
m_text.setPosition(10, 10);
m_text.setFillColor(sf::Color::White);
}
virtual bool update() {
m_count++;
m_text.setString("MenuState: " + std::to_string(m_count));
return true;
}
virtual bool draw(sf::RenderTarget &target) const{
target.draw(m_text);
return true;
}
virtual bool handleEvent(sf::Event e, std::vector<State*> &stack){
if (e.type == sf::Event::KeyPressed){
if (e.key.code == sf::Keyboard::P){
stack.push_back(new PauseState());
return true;
}
}
return true;
}
private:
sf::Font m_font;
sf::Text m_text;
unsigned int m_count;
};
PauseState
class PauseState : public State{
public:
PauseState(){
sf::Font f;
m_font.loadFromFile("Roboto-Regular.ttf");
m_text.setFont(m_font);
m_text.setString("PauseState");
m_text.setPosition(10, 10);
m_text.setFillColor(sf::Color::White);
}
virtual bool update() {
// By returning false, we prevent States UNDER Pause to update too
return false;
}
virtual bool draw(sf::RenderTarget &target) const{
target.draw(m_text);
// By returning false, we prevent States UNDER Pause to draw too
return false;
}
virtual bool handleEvent(sf::Event e, std::vector<State*> &stack){
if (e.type == sf::Event::KeyPressed){
if (e.key.code == sf::Keyboard::Escape){
stack.pop_back();
return true;
}
}
return false;
}
private:
sf::Font m_font;
sf::Text m_text;
};
By the way, while I was doing this, I notice that you must have the fonts as an attribute of the class in order to keep the reference. If not, when your text is drawn, its font is lost ant then it fails. Another way to face this is using a resource holder, which is much more efficient and robust.
Said this, our main will look like:
Main
int main() {
// Create window object
sf::RenderWindow window(sf::VideoMode(720, 720), "OpenTMS");
// Set window frame rate
window.setFramerateLimit(60);
std::vector<State*> stack;
// Create menu
stack.push_back(new MenuState());
// Main window loops
while (window.isOpen()) {
// Create events object
sf::Event event;
// Loop through events
while (window.pollEvent(event)) {
// Close window
if (event.type == sf::Event::Closed) {
window.close();
}
handleEventStack(event, stack);
}
updateStack(stack);
// Clear window
window.clear(sf::Color::Black);
drawStack(window, stack);
// Display window contents
window.display();
}
return 0;
}
The stack functions are simple for-loop but, with the detail that iterate the vector backwards. This is the way to imitate that stack behavior, starting from top (size-1 index) and ending at 0.
Stack functions
void handleEventStack(sf::Event e, std::vector<State*> &stack){
for (int i = stack.size()-1; i >=0; --i){
if (!stack[i]->handleEvent(e, stack)){
break;
}
}
}
void updateStack(std::vector<State*> &stack){
for (int i = stack.size() - 1; i >= 0; --i){
if (!stack[i]->update()){
break;
}
}
}
void drawStack(sf::RenderTarget &target, std::vector<State*> &stack){
for (int i = stack.size() - 1; i >= 0; --i){
if (!stack[i]->draw(target)){
break;
}
}
}
You can learn more about StateStacks and gamedev in general with this book

Changing the object a pointer points to in c++

I'm creating a game, and I have a array of pointers to hero objects. Only one hero should be active at a time, so I have a separate pointer to the active hero.
If it matters, I'm using Qt, and the Hero class inherits from QgraphicsItem.
it is important that I am able to switch between Hero's, so I've written the following piece of code:
void Game::toggleHero()
{
if(activeHero==hero[0])
{
activeHero =hero[1];
hero[1]->setFocus();
}
else if(activeHero == hero[1])
{
activeHero = hero[0];
hero[0]->setFocus();
}
}
The problem is, that that method does what I expect it to do, but only the first time it is called. The activeHero newer changes back to the original object.
The array of Hero objects are members of a game class, and are initialized in the game object's constructor. The only time activeHero is ever referenced is when a hook item is added, or when the setView() method is called.
class Game : public QGraphicsView
{
public:
Game();
Terrain *ground[63];
Wall *wall[63];
Ladder *ladder[63];
Bushes * bush;
QGraphicsScene * scene;
Hero *hero[1];
Hero *activeHero;
void buildLevel(int level);
void setView();
void toggleHero();
};
From the game object's constructor:
//add hero
hero[0] = new Hero();
hero[0]->setFlag(QGraphicsItem::ItemIsFocusable);
hero[0]->setFocus();
scene->addItem(hero[0]);
hero[0]->setPos(5,1200);
// activeHero = hero[0];
hero[1] = new Hero();
hero[1]->setFlag(QGraphicsItem::ItemIsFocusable);
hero[1]->setFocus();
scene->addItem(hero[1]);
hero[1]->setPos(10,1300);
activeHero = hero[0];
The setView() method checks if the active hero is in view, and if it isn't it moves the view so that the activeHero is at the correct spot relative to the view.
void Game::setView()
{
if((!(activeHero->pos().y()+activeHero- >rect().height()/2==sceneRect().y()+sceneRect().height()/2-400)&&((activeHero- >y()+activeHero->rect().height()/2-400>0))&&(activeHero->y()+activeHero->rect().height()-400<1600))){
setSceneRect(sceneRect().x(),activeHero->pos().y()+activeHero->rect().height()/2-400,800,600);
}
if((!(activeHero->pos().x()+activeHero->rect().width()/2==sceneRect().x()+sceneRect().width()/2))&&((activeHero->x()+activeHero->rect().width()-450>0)&&(activeHero->x()+activeHero->rect().width()+350<3200))){
setSceneRect(activeHero->pos().x()+activeHero->rect().width()/2-400,sceneRect().y(),800,600);
}
And finally, a Hook object uses the activeHero to position itself in the scene.
Hook::Hook(int direction)
{
hookdirection = direction;
switch(hookdirection)
{
case -1:
{
setRect(0,0,16,16);
setPos(game->activeHero->pos());
break;
}
case 1:
{
setRect(0,0,16,16);
setPos(game->activeHero->pos().x()+game->activeHero->rect().width()- rect().width(),game->activeHero->pos().y());
break;
}
default:
{
setRect(0,0,16,16);
break;
}
}
timer = new QTimer;
connect(timer,SIGNAL(timeout()),this,SLOT(move()));
timer->start(10);
lineItem = new QGraphicsLineItem();
lineItem->setParentItem(this);
game->activeHero->scene()->addItem(lineItem);
}
The objective of the toggleHero method is to switch between hero[0], and hero[1] so that each of them is the activeHero in the methods above.
If hero is a pointer you need decide whether to compare the heroes (eg. by name with overloaded operators) or the adresses. So it should be:
if(*activeHero==hero[0])
{
*activeHero =hero[1];
hero[1]->setFocus();
}
else if(*activeHero == hero[1])
{
*activeHero = hero[0];
hero[0]->setFocus();
}
or it should be
if(activeHero==&hero[0])//address of hero[0]
{
activeHero =&hero[1];
hero[1]->setFocus();
}
else if(activeHero == &hero[1])
{
activeHero = &hero[0];
hero[0]->setFocus();
}

Spawning waves of enemies C++

I'm creating a simple game with qt 5.0.1. It's something like Warblade.
I have problem with creating waves of enemies.
int k;
int pos = 100;
for (k = 0; k < 5; k++)
{
pos = 100;
for (int i = 0; i < 9; i++)
{
player->spawn_in_pos(pos);
pos += 100;
}
//QThread::sleep(2);
}
When i use sleep() function, my game just can't run. It's waiting for loop finish and then it shows.
I'm also dealing with second option:
QTimer * timer = new QTimer();
QObject::connect( timer, SIGNAL(timeout()), player, SLOT(spawn_in_pos(pos)) );
timer->start(450);
But it looks like SLOT can't get the position.
Edit:
I just did what #ddriver said, and that helped me a lot.
Now I'm getting some 'laggy' style enemies movement.
Edit2:
I'm moving my enemies down like this:
setPos(x(),y()+1);
with that timer:
// connect
QTimer * timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(move()));
// start the timer
timer->start(10);
It looks like very smooth movement but probably +1 pixel down and a 10 timer is to less:((
I'm not sure what you are trying to achieve, but in your second option, you cannot get the position, because the timeout doesn't send it.
The signal is timeout(void) and your slot expects an parameter. I guess you lack some basic understanding of the signal/slot mechanism.
The QT Documentation is pretty neat:
http://doc.qt.io/qt-5/signalsandslots.html
And if you just want to create a game out of nothing, here you can find a little tutorial, how to write games in QT:
https://www.youtube.com/watch?v=8ntEQpg7gck
Calling sleep is going to stop the thread from processing anything, which is not what you want to do.
Using C++ 11, you can use the QTimer with a lambda function like this: -
int pos = 100;
int nextWaveTime = 2000; // 2 seconds per wave
for (k = 0; k < 5; k++) // 5 waves of enemies
{
for (int i = 0; i < 9; i++) // 9 enemies per wave
{
QTimer * timer = new QTimer();
timer->setSingleShot(true);
pos = pos + (100*i); // set the pos, which is captured by value, in the lambda function
QObject::connect( timer, QTimer::timeout, [=](){
player->spawn_in_pos(pos);
timer->deleteLater(); // must cleanup the timer
});
timer->start(450 + (k*nextWaveTime));
}
}
In order to pass parameters with signals and slots in Qt, the signal parameters must match the parameters of the slot (or function since Qt 5).
One way to solve the issue is to use a lambda as in TheDarkKnight's answer.
What I would suggest is to use encapsulation - you could create a Spawner object, dedicated to spawning enemies and keep the position internal to it. This way the spawner will manage the position, and you can have something like Spawner::createWave() slot with no parameters, since the position is internal. Then setup the timer and connect it to createWave() and you are set.
Also it is a very bad idea to hardcode stuff like that, you really need more flexibility, the option to change enemy and wave count, the wave time as well as the screen width, so that your game can change those things as it gets harder.
class Spawner : public QObject {
Q_OBJECT
public:
Spawner(int wCount = 5, int eCount = 9, int time = 2000, int sWidth = 1000)
: waveCount(wCount), enemyCount(eCount), currentWave(0), screenWidth(sWidth) {
timer.setInterval(time);
connect(&timer, SIGNAL(timeout()), this, SLOT(createWave()));
}
void set(int wCount, int eCount, int time) {
timer.setInterval(time);
waveCount = wCount;
enemyCount = eCount;
}
void changeWidth(int w) { screenWidth = w; }
public slots:
void start() { timer.start(); }
void stop() {
timer.stop();
currentWave = 0;
}
private slots:
void createWave() {
int pos = screenWidth / (enemyCount + 1);
int step = pos;
for (int i = 0; i < enemyCount; ++i) {
Game::spawnEnemyAt(pos);
pos += step;
}
if (++currentWave >= waveCount) stop();
}
private:
QTimer timer;
int waveCount, enemyCount, currentWave, screenWidth;
};
Create a Spawner object and connect the game new level to start() - it will span the given number waves of enemies evenly across the game screen, when you finish the waves off, you adjust the spawner settings and start a new level.
That encapsulation will come in handy later on as your game becomes less of a test and more like a real game - with increasing difficulty, changing spawning and attack patterns and so on. So it is a good idea to implement it right from the start and build upon a good and flexible design rather than going back and changing stuff around, which may break other code. You really don't want to start without a good design and make design changes later. Thus the need to encapsulate functionality and responsibility and just connect the pieces rather than building on a pile of spaghetti code. In this line of thought, I noticed you are using player->spawn_in_pos(pos); - which is an example of bad design, as spawning should be a responsibility of the Game class, not the Player class. A good design is not only flexible, but also clean. The Spawner object is only responsible for spawning waves of enemies, and its visible interface is limited to start(), stop() and set().
Edit:
class Game : public QObject {
Q_OBJECT
public:
Game() {
if (!scene) scene = new QGraphicsScene(this);
connect(this, SIGNAL(newLevel()), &spawner, SLOT(start()));
}
static void spawnEnemyAt(int x = 0) {
scene->addItem(new Enemy(x, 0));
qDebug() << "enemy created";
}
public slots:
void newGame() {
// initialize game
emit newLevel(); // begin spawning
}
void onLevelEnd() {
// spawner.set(new level settings);
emit newLevel();
}
void onGameEnded() {
// ...
}
signals:
void newLevel();
private:
Spawner spawner;
static QGraphicsScene * scene;
};
// in game.cpp
QGraphicsScene * Game::scene = nullptr;
If you don't want to use static members, you can make spawnEnemyAt() and scene instance members, but then you will have to pass the Game instance to the Spawner in the constructor so that you have a reference to the game the spawner operates on and use game->spawnEnemyAt() instead. This way you can create multiple games with their own dedicated scenes. Or parent the spawner to the game and cast the spawner's parent() to a Game * to access the game instance which is a little hacky, but saves on the extra member by reusing the parent.

replaceScene() messes up a public variable

I am porting a game from cocos2d-iphone 2.x to cocos2d-x 3.x.
Have to solve a few problems, including a major crash - the subject of this post.
It has been determined that the crash happens because SOMETIMES, my replaceScene call results in a messed-up important public variable.
My class:
class Player : public cocos2d::Sprite
{
public:
....
cocos2d::Vec2 desiredPosition;
....
My Layer methods:
Scene* GameLevelLayer::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = GameLevelLayer::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
bool GameLevelLayer::init()
{
// super init first
if ( !Layer::init() )
{
return false;
}
....
player = (Player*) cocos2d::Sprite::create("sprite_idle_right#2x.png");
player->setPosition(Vec2(100, 50));
player->desiredPosition = player->getPosition();
....
this->schedule(schedule_selector(GameLevelLayer::update), 1.0/60.0);
....
return true;
}
void GameLevelLayer::endGame(bool won) {
....
MenuItem* display;
if (currentLevel < lastLevel && won) {
++currentLevel;
display = MenuItemImage::create("next.png" ,"next.png" ,"next.png",
CC_CALLBACK_1(GameLevelLayer::replaceSceneCallback, this));
} else {
// Lost the game
currentLevel = 1;
display = MenuItemImage::create("replay.png", "replay.png", "replay.png",
CC_CALLBACK_1(GameLevelLayer::replaceSceneCallback, this));
}
....
}
void GameLevelLayer::replaceSceneCallback(Ref* sender) {
Director::getInstance()->replaceScene(this->createScene());
}
The member being messed is the desiredPosition. It is changed inside update() method. The problem is that update() gets an already messed-up desired position. It is only messed-up after a scene was being replaced. The problem happens once in 10 runs, or so. It even appears that when update() is called first time after the scene has been replaced, desiredPosition set to some garbage. is I was unable to learn more.
My Player class does not have a separate constructor.
Please advise.
I forgot to initialize another instance variable. That instance variable is used to calculate the desiredPosition.