So I am working on this game and I want to implement a score system.
This is the first time I am working with Cocos2d.
I tried a couple of things but didn't really had succes.
I would like it if for each enemy that gets removed from the scene a int with the name score would be increased by one.
Do you guys have some suggestions ?
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
USING_NS_CC;
#define BACKGROUND_MUSIC_SFX "bee.mp3"
#define PEW_PEW_SFX "splatter.mp3"
// These bit masks define the physics categories; monster + projectile with two values to specify no type or all types, I use this to see what objects are allowed to collide
enum class PhysicsCategory {
None = 0,
Monster = (1 << 0), // 1
Projectile = (1 << 1), // 2
All = PhysicsCategory::Monster | PhysicsCategory::Projectile // 3
};
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object, turn psysics on
auto scene = Scene::createWithPhysics();
scene->getPhysicsWorld()->setGravity(Vec2(0,0));
scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_NONE);
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer child to scene
scene->addChild(layer);
// return scene
return scene;
}
// initialize instance
bool HelloWorld::init()
{
// call the super class’s init method, if succeeds proceed HelloWorldScene‘s setup
if ( !Layer::init() ) {
return false;
}
// get window bounds using game Director singleton.
auto origin = Director::getInstance()->getVisibleOrigin();
auto winSize = Director::getInstance()->getVisibleSize();
// create a DrawNode to draw a green rectangle that fills the screen
auto background = DrawNode::create();
background->drawSolidRect(origin, winSize, Color4F(0.0,0.6,0.0,0.7));
this->addChild(background);
// create the player sprite, position 10% from the left edge of the screen, centered vertically
_player = Sprite::create("player.png");
_player->setPosition(Vec2(winSize.width * 0.1, winSize.height * 0.5));
this->addChild(_player);
// seed the random number generator
srand((unsigned int)time(nullptr));
this->schedule(schedule_selector(HelloWorld::addMonster), 1.5);
auto eventListener = EventListenerTouchOneByOne::create();
eventListener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(eventListener, _player);
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegan, this);
this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener, this);
SimpleAudioEngine::getInstance()->playBackgroundMusic(BACKGROUND_MUSIC_SFX, true);
return true;
}
void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
return;
#endif
Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}
void HelloWorld::addMonster(float dt) {
auto monster = Sprite::create("monster.png");
//Create a PhysicsBody for the sprite, Physics bodies represent the object in Cocos2d physics simulation.
auto monsterSize = monster->getContentSize();
auto physicsBody = PhysicsBody::createBox(Size(monsterSize.width , monsterSize.height),
PhysicsMaterial(0.1f, 1.0f, 0.0f));
//Set the sprite to be dynamic. physics engine will not apply forces to the bear.
physicsBody->setDynamic(true);
// set the category, collision and contact test bit masks:
physicsBody->setCategoryBitmask((int)PhysicsCategory::Monster);
physicsBody->setCollisionBitmask((int)PhysicsCategory::None);
physicsBody->setContactTestBitmask((int)PhysicsCategory::Projectile);
monster->setPhysicsBody(physicsBody);
// create a bear sprite and place it offscreen to the right, random y position
auto monsterContentSize = monster->getContentSize();
auto selfContentSize = this->getContentSize();
int minY = monsterContentSize.height/2;
int maxY = selfContentSize.height - monsterContentSize.height/2;
int rangeY = maxY - minY;
int randomY = (rand() % rangeY) + minY;
monster->setPosition(Vec2(selfContentSize.width + monsterContentSize.width/2, randomY));
this->addChild(monster);
// random duration for bear, each bear will move the same distance across the screen, varying the duration results in bears with random speeds.
int minDuration = 2.0;
int maxDuration = 4.0;
int rangeDuration = maxDuration - minDuration;
int randomDuration = (rand() % rangeDuration) + minDuration;
// move the bear across the screen.
auto actionMove = MoveTo::create(randomDuration, Vec2(-monsterContentSize.width/2, randomY));
auto actionRemove = RemoveSelf::create();
monster->runAction(Sequence::create(actionMove,actionRemove, nullptr));
}
bool HelloWorld::onTouchBegan(Touch *touch, Event *unused_event) {
// 1 - get the _player object
//auto node = unused_event->getCurrentTarget();
// coordinate of the click within the scene’s coordinate system, calculate the offset of this point from the current position. vector math.
Vec2 touchLocation = touch->getLocation();
Vec2 offset = touchLocation - _player->getPosition();
// If offset‘s x value is negative, the player tries to shoot backwards,return without firing.
if (offset.x < 0) {
return true;
}
// Create projectile, add to screen
auto projectile = Sprite::create("projectile.png");
projectile->setPosition(_player->getPosition());
auto projectileSize = projectile->getContentSize();
auto physicsBody = PhysicsBody::createCircle(projectileSize.width/2 );
physicsBody->setDynamic(true);
physicsBody->setCategoryBitmask((int)PhysicsCategory::Projectile);
physicsBody->setCollisionBitmask((int)PhysicsCategory::None);
physicsBody->setContactTestBitmask((int)PhysicsCategory::Monster);
projectile->setPhysicsBody(physicsBody);
this->addChild(projectile);
// call normalize() to convert the offset into a unit vector, which is a vector of length 1. Multiplying that by 1000 that points in the direction of the user’s tap.
offset.normalize();
auto shootAmount = offset * 1000;
// Adding the vector to the projectile’s position gives the target position.
auto realDest = shootAmount + projectile->getPosition();
// move the projectile to the target position, remove after 5 sec.
auto actionMove = MoveTo::create(5.0f, realDest);
auto actionRemove = RemoveSelf::create();
projectile->runAction(Sequence::create(actionMove,actionRemove, nullptr));
SimpleAudioEngine::getInstance()->playEffect(PEW_PEW_SFX);
return true;
}
// PhysicsContact passed to this method is collision, remove at the end
bool HelloWorld::onContactBegan(PhysicsContact &contact) {
auto nodeA = contact.getShapeA()->getBody()->getNode();
auto nodeB = contact.getShapeB()->getBody()->getNode();
nodeA->removeFromParent();
nodeB->removeFromParent();
return true;
}
I'm not sure that I have fully understood your question, but it seems that you want to create a label to show the current score.
Of course you will need a variable to keep track of your score. Let's assume the variable is an integer and it's called m_score.
You will then need to create and add a Label to your scene. In your init() function add:
m_scoreLabel = Label::createWithTTF(std::to_string(m_score), "fonts/Arial.ttf", 36);
this->addChild(m_scoreLabel);
You will need m_scoreLabel to keep a reference to your Label so that you will be able to change its display text later on. Now, whenever you change the score, first update the value of m_score and then to update the text shown by the label call:
m_scoreLabel->setString(std::to_string(m_score));
Related
I'm trying to visualize a vtk unstructuredgrid mesh.
In order to get the coordinate of a point of my mesh I use the vtk interactor.
I'm able to get the point coordinate by selecting the point using OnRightButtonDown() "overrid"
. However, I loose control of my window . means I can not rotate, translate or zoom my mesh.
I tried to do use OnRightButtonDoubleClick() but this doesn't seem to work . Any idea how may I
get the node coordinate using the interactor without affecting the mouse event behavior or how to re-initialize it when the mouse button is Up...
foo
{
...
// vtk visualization
container = new QWidget(ui->graphicsView);
qvtkWidget = new QVTKOpenGLNativeWidget(container);
...
//Create and link the mapper actor and renderer together.
mapper = vtkSmartPointer<vtkDataSetMapper>::New();
actor = vtkSmartPointer<vtkActor>::New();
renderer = vtkSmartPointer<vtkRenderer>::New();
...
// add elements nodes
...
mapper->SetInputData(eleNodeIdsPtr);
actor->SetMapper(mapper);
renderer->AddActor(actor);
// ste up camera
renderer->SetBackground(0.06, 0.2, 0.5);
double pos[3] = { 0, 0.2, 1 };
double focalPoint[3] = { 0, 0, 0 };
double viewUp[3] = { 1, 1, 1 };
renderer->GetActiveCamera()->SetPosition(pos);
renderer->GetActiveCamera()->SetFocalPoint(focalPoint);
renderer->GetActiveCamera()->SetViewUp(viewUp);
renderer->GetActiveCamera()->Zoom(0.5);
//Add render
qvtkWidget->GetRenderWindow()->AddRenderer(renderer);
qvtkWidget->show();
// Select node
renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(qvtkWidget-
>GetRenderWindow());
vtkNew<InteractorStyle2> style;
renderWindowInteractor->SetInteractorStyle(style);
style->eleNodeIdsPtr = eleNodeIdsPtr;
style->xyzGlobalPtr = xyzGlobalPtr;
renderWindowInteractor->Initialize();
}
// Define interaction style
class InteractorStyle2 : public vtkInteractorStyleTrackballActor
{
public:
static InteractorStyle2* New();
vtkTypeMacro(InteractorStyle2, vtkInteractorStyleTrackballActor);
vtkNew<vtkNamedColors> color;
...
void OnLeftButtonDown() //...>>>This doesn't work!!
{
}
void OnLeftButtonDown() override // .. this work but the I can't controle the transformation anymore!!
{
this->PointPicker = vtkSmartPointer<vtkPointPicker>::New();
// Get the selected point
int x = this->Interactor->GetEventPosition()[0];
int y = this->Interactor->GetEventPosition()[1];
this->FindPokedRenderer(x, y);
this->PointPicker->Pick(this->Interactor->GetEventPosition()[0],
this->Interactor->GetEventPosition()[1],
0, // always zero.
this->Interactor->GetRenderWindow()
->GetRenderers()
->GetFirstRenderer());
if (this->PointPicker->GetPointId() >= 0)
{
this->StartPan();
this->SelectedPoint = this->PointPicker->GetPointId();
double p[3];
this->eleNodeIdsPtr->GetPoint(this->SelectedPoint, p);
std::cout << "p: " << p[0] << " " << p[1] << " " << p[2] << std::endl;
}
}
vtkSmartPointer<vtkPointPicker> PointPicker;
vtkIdType SelectedPoint;
vtkSmartPointer<vtkUnstructuredGrid> eleNodeIdsPtr;
vtkSmartPointer< vtkPoints > xyzGlobalPtr;
}
vtkStandardNewMacro(InteractorStyle2);
}
You should call the OnLeftButtonDown (or up) of the parent class, by adding this line at the end of your method impl:
vtkInteractorStyleTrackballActor::OnLeftButtonDown();
A similar example
I am using Unreal Engine 4.25. I am trying to move an Actor, which is a sphere, in a circular path. However the sphere only moves back and forth in a straight line.
Here is MyActor.cpp code:
#include "MyActor.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Components/StaticMeshComponent.h"
// Sets default values
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
RootComponent = Root;
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
Mesh->AttachTo(Root);
Dimensions = FVector(30, 0, 0);
AxisVector = FVector(0.01, 0.01, 0);
Location = FVector(0.1, 0.1, 0.1);
Multiplier = 50.f;
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// Updates the angle of the object
AngleAxis += DeltaTime * Multiplier;
if (AngleAxis >= 360.0f)
{
AngleAxis = 0;
}
//Rotates around axis
FVector RotateValue = Dimensions.RotateAngleAxis(AngleAxis, AxisVector);
Location.X += RotateValue.X;
Location.Y += RotateValue.Y;
Location.Z += RotateValue.Z;
SetActorLocation(Location, false, 0, ETeleportType::None);
}
Please let me know if I need to add any more information.
Disclaimer: I am completely new to UE4. My apologies if it is a dumb question.
I'm a novice programmer trying to follow a tuturial on recreating a game on Steam called Timber(I believe). The program was working fine and I had almost completed the tutrial, but I ran into trouble when I added the code:
for (int i = 0 < NUM_BRANCHES; i++;)
{
branches[i].setTexture(textureBranch);
branches[i].setPosition(-2000, -2000);
// Set the sprite's origin to dead center
// We can then spin it around without changing its position
branches[i].setOrigin(220, 20);
}
Visual Studio says: branches[i].setPosition(-2000, -2000); Unhandled exception thrown: write access violation.
this was 0x59AF28. occurred
I'll also post the full code, apologies that it's a bit messy.
#include "stdafx.h"
#include <sstream>
#include <SFML\Graphics.hpp>
using namespace sf;
// Function declaration
void updateBranches(int seed);
const int NUM_BRANCHES = 6;
Sprite branches[NUM_BRANCHES];
// Where is the player/branch?
// Left or right
enum class side{ LEFT, RIGHT, NONE };
side branchPositions[NUM_BRANCHES];
int main()
{
// Creates a video mode object
VideoMode vm(1920, 1080);
// Creates and opens a window for the game
RenderWindow window(vm, "Timber!!!", Style::Fullscreen);
// Create a texture to hold a graphic on the GPU
Texture textureBackground;
// Load a graphic into the texture
textureBackground.loadFromFile("graphics/background.png");
// Create a sprite
Sprite spriteBackground;
// Attach the texture to the sprite
spriteBackground.setTexture(textureBackground);
// Set the spriteBackground to cover the screen
spriteBackground.setPosition(0, 0);
// Make a tree sprite
Texture textureTree;
textureTree.loadFromFile("graphics/tree.png");
Sprite spriteTree;
spriteTree.setTexture(textureTree);
spriteTree.setPosition(810, 0);
// Prepare the bee
Texture textureBee;
textureBee.loadFromFile("graphics/bee.png");
Sprite spriteBee;
spriteBee.setTexture(textureBee);
spriteBee.setPosition(0, 450);
// Is the be currently moving?
bool beeActive = false;
// How fast can the bee fly
float beeSpeed = 0.0f;
// Make 3 cloud sprites from 1 texture
Texture textureCloud;
// Load 1 new texture
textureCloud.loadFromFile("graphics/cloud.png");
// 3 new sprites with the same texture
Sprite spriteCloud1;
Sprite spriteCloud2;
Sprite spriteCloud3;
spriteCloud1.setTexture(textureCloud);
spriteCloud2.setTexture(textureCloud);
spriteCloud3.setTexture(textureCloud);
// Position the clouds off screen
spriteCloud1.setPosition(0, 0);
spriteCloud2.setPosition(0, -150);
spriteCloud3.setPosition(0, -300);
// Are the clouds currently on the screen?
bool cloud1Active = false;
bool cloud2Active = false;
bool cloud3Active = false;
// How fast is each cloud?
float cloud1Speed = 0.1f;
float cloud2Speed = 0.2f;
float cloud3Speed = 0.3f;
// Variables to control time itself
Clock clock;
// Time bar
RectangleShape timeBar;
float timeBarStartWidth = 400;
float timeBarHeight = 80;
timeBar.setSize(Vector2f(timeBarStartWidth, timeBarHeight));
timeBar.setFillColor(Color::Red);
timeBar.setPosition((1920 / 2) - timeBarStartWidth / 2, 980);
Time gameTimeTotal;
float timeRemaining = 6.0f;
float timeBarWidthPerSecond = timeBarStartWidth / timeRemaining;
// Track whether the game is running
bool paused = true;
// Draw some text
int score = 0;
sf::Text messageText;
sf::Text scoreText;
// Font
Font font;
font.loadFromFile("fonts/KOMIKAP_.ttf");
// Set the font of our message
messageText.setFont(font);
scoreText.setFont(font);
// Assign the actual message
messageText.setString("Press Enter to Start!");
scoreText.setString("score = 0");
// Make text really big
messageText.setCharacterSize(75);
scoreText.setCharacterSize(100);
// Choose a color
messageText.setFillColor(Color::White);
scoreText.setFillColor(Color::Black);
// Position the text
FloatRect textRect = messageText.getLocalBounds();
messageText.setOrigin(textRect.left +
textRect.width / 2.0f,
textRect.top +
textRect.height / 2.0f);
messageText.setPosition(1920 / 2.0f, 1080 / 2.0f);
scoreText.setPosition(20, 20);
// Prepare 6 branches
Texture textureBranch;
textureBranch.loadFromFile("graphics/branch.png");
// Set the texture for each branch sprite
for (int i = 0 < NUM_BRANCHES; i++;)
{
branches[i].setTexture(textureBranch);
branches[i].setPosition(-2000, -2000);
// Set the sprite's origin to dead center
// We can then spin it around without changing its position
branches[i].setOrigin(220, 20);
}
while (window.isOpen())
{
/*
**************
Handles the player input
**************
*/
if (Keyboard::isKeyPressed(Keyboard::Escape))
{
window.close();
}
// Start the game
if (Keyboard::isKeyPressed(Keyboard::Return))
{
paused = false;
// Reset the time and the score
score = 0;
timeRemaining = 5;
}
/*
**************
Update the scene
**************
*/
if (!paused)
{
// Measure time
Time dt = clock.restart();
// Subtract from the amount of time remaining
timeRemaining -= dt.asSeconds();
// size up the time bar
timeBar.setSize(Vector2f(timeBarWidthPerSecond *
timeRemaining, timeBarHeight));
if (timeRemaining <= 0.0f)
{
// Pause the game
paused = true;
// Change the message shown to the player
messageText.setString("Out of time");
// Reposition the text base on its new size
FloatRect textRect = messageText.getLocalBounds();
messageText.setOrigin(textRect.left +
textRect.width / 2.0f,
textRect.top +
textRect.height / 2.0f);
messageText.setPosition(1920 / 2.0f, 1080 / 2.0f);
}
// Setup the bee
if (!beeActive)
{
// How fast is the bee
srand((int)time(0) * 10);
beeSpeed = (rand() % 400) + 350;
// How high is the bee
srand((int)time(0) * 10);
float height = (rand() % 650) + 850;
spriteBee.setPosition(1921, height);
beeActive = true;
}
else
// Move the bee
{
spriteBee.setPosition(
spriteBee.getPosition().x -
(beeSpeed * dt.asSeconds()),
spriteBee.getPosition().y);
// Has the bee reached the right hand edge of the screen?
if (spriteBee.getPosition().x < -100)
{
// Set it up ready to be a whole new bee next frame
beeActive = false;
}
}
// Manage the clouds
// Cloud 1
if (!cloud1Active)
{
// How fast is the cloud
srand((int)time(0) * 10);
cloud1Speed = (rand() % 150);
// How high is the cloud
srand((int)time(0) * 10);
float height = (rand() % 200);
spriteCloud1.setPosition(-300, height);
cloud1Active = true;
}
else
{
spriteCloud1.setPosition(
spriteCloud1.getPosition().x +
(cloud1Speed * dt.asSeconds()),
spriteCloud1.getPosition().y);
// Has the cloud reached the right hand edge of the screen?
if (spriteCloud1.getPosition().x > 1920)
{
// Set it up ready to be a whole new cloud next frame
cloud1Active = false;
}
}
// Cloud 2
if (!cloud2Active)
{
// How fast is the cloud
srand((int)time(0) * 20);
cloud2Speed = (rand() % 200);
// How high is the cloud
srand((int)time(0) * 20);
float height = (rand() % 300);
spriteCloud2.setPosition(-200, height);
cloud2Active = true;
}
else
{
spriteCloud2.setPosition(
spriteCloud2.getPosition().x +
(cloud1Speed * dt.asSeconds()),
spriteCloud2.getPosition().y);
// Has the cloud reached the right hand edge of the screen?
if (spriteCloud2.getPosition().x > 1920)
{
// Set it up ready to be a whole new cloud next frame
cloud2Active = false;
}
}
// Cloud 3
if (!cloud3Active)
{
// How fast is the cloud
srand((int)time(0) * 30);
cloud3Speed = (rand() % 250);
// How high is the cloud
srand((int)time(0) * 30);
float height = (rand() % 150);
spriteCloud3.setPosition(-100, height);
cloud3Active = true;
}
else
{
spriteCloud3.setPosition(
spriteCloud3.getPosition().x +
(cloud1Speed * dt.asSeconds()),
spriteCloud3.getPosition().y);
// Has the cloud reached the right hand edge of the screen?
if (spriteCloud3.getPosition().x > 1920)
{
// Set it up ready to be a whole new cloud next frame
cloud3Active = false;
}
}
// Update the score text
std::stringstream ss;
ss << "Score = " << score;
scoreText.setString(ss.str());
// Update the branch sprites
for (int i = 0; i < NUM_BRANCHES; i++)
{
float height = i * 150;
if (branchPositions[i] == side::LEFT)
{
// Move the sprite to the left side
branches[i].setPosition(610, height);
// Flip the sprite around the other way
branches[i].setRotation(180);
}
else if (branchPositions[i] == side::RIGHT)
{
// Move the sprite to the right side
branches[i].setPosition(1330, height);
// Set the sprite rotation to normal
branches[i].setRotation(0);
}
else
{
// Hide the branch
branches[i].setPosition(3000, height);
}
}
} // End if(!paused)
/*
**************
Draw the scene
**************
*/
// Clear everything from the last frame
window.clear();
// Draw our game scene here
window.draw(spriteBackground);
// Draw the clouds
window.draw(spriteCloud1);
window.draw(spriteCloud2);
window.draw(spriteCloud3);
// Draw the branches
for (int i = 0; i < NUM_BRANCHES; i++)
{
window.draw(branches[i]);
}
// Draw the tree
window.draw(spriteTree);
// Draw the insect
window.draw(spriteBee);
// Draw the score
window.draw(scoreText);
//Draw the timebar
window.draw(timeBar);
if (paused)
{
// Draw our message
window.draw(messageText);
}
// Show everyting we just drew
window.display();
}
return 0;
}
// Function definition
void updateBranches(int seed)
{
// Move all the branches down on place
for (int j = NUM_BRANCHES - 1; j > 0; j--)
{
branchPositions[j] = branchPositions[j - 1];
}
// Spawn a new branch at postion 0
// LEFt, RIGHT, NONE
srand((int)time(0) + seed);
int r = (rand() % 5);
switch (r)
{
case 0:
branchPositions[0] = side::LEFT;
break;
case 1:
branchPositions[0] = side::RIGHT;
break;
default:
branchPositions[0] = side::NONE;
break;
}
}
setPosition() requires an sf::Vector2f. So you could fix your code by changing that line to:
branches[i].setPosition(sf::Vector2f(-2000, -2000))
In my gaming context, the sprite is a smiling baby, when the baby is touched, the current sprite image changed to a crying baby, then after 3 seconds of crying sound effect ended, the sprite will restore to the smiling baby image.
My problem is
How to swap back with the previous image?
I have changed the smiling baby to a crying one, but i have no idea how could I swap it with the original smiling one?
How to ensure one click at one time?
Each time the baby sprite is touched, the audio will start to play which is not ideal, because I hope the event function is only called after the previous event process is finished.
here is my code, and thank you a lot!
bool HelloWorld::init()
{
// 1. super init first
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
//add background scene
auto backgroundSprite = Sprite :: create("scene001.jpg");
backgroundSprite->setAnchorPoint(Vec2(0,0));
backgroundSprite->setScaleX((visibleSize.width / backgroundSprite->getContentSize().width) * 1);
backgroundSprite->setScaleY((visibleSize.height / backgroundSprite->getContentSize().height) * 1);
addChild(backgroundSprite);
//add smileBaby sprite
auto smileBabySprite = Sprite :: create("figure001.png");
smileBabySprite -> setPosition(Vec2(500,400));
addChild(smileBabySprite);
//add crying audio
auto audio = CocosDenshion::SimpleAudioEngine::getInstance();
auto babyListener = EventListenerTouchOneByOne::create();
babyListener -> onTouchBegan = [smileBabySprite, audio](Touch* touch, Event* event)
{
//change smileBaby sprite to cryingBaby sprite
smileBabySprite->setTexture(CCTextureCache::sharedTextureCache()->addImage("figure002.png"));
audio -> playEffect("babycry.mp3",false,1.0f,1.0f,1.0f);
return true;
};
babyListener -> onTouchEnded=[smileBabySprite](Touch* touch, Event* event )
{
};
_eventDispatcher -> addEventListenerWithSceneGraphPriority(babyListener, this);
return true;
}
What you want is to keep state about if the baby is crying or not. The best thing to do is keep this logic in a custom Node subclass.
Here is some (almost pseudo-)code to get you started:
Baby.h:
#pragma once
#include "cocos2d.h"
class Baby : public cocos2d::Node
{
private:
cocos2d::Sprite *_sprite; // Weak reference
bool _crying;
float _cryingTime;
public:
CREATE_FUNC(Baby);
protected:
virtual bool init() override;
virtual void update(float delta) override;
public:
void touched();
bool isInside(cocos2d::Touch *touch) const
protected:
void setSprite();
};
Baby.cpp:
#include "Baby.h"
USING_NS_CC;
bool Baby::init()
{
if (!Node::init())
return false;
_crying = false;
setSprite();
scheduleUpdate();
return true;
}
void Baby::update(float delta)
{
Node::update(delta);
if (_crying) {
_cryingTime -= delta;
if (_cryingTime <= 0.0f) {
_crying = false;
setSprite();
}
}
}
void Baby::touched()
{
if (_crying)
return; // Already crying
_crying = true;
_cryingTime = 3.0f; // Length of audio, I guess?
setSprite();
// Start crying sound here
}
bool Baby::isInside(Touch *touch) const
{
Vec2 locationInNode = _sprite->convertToNodeSpace(touch->getLocation());
Size size = _sprite->getContentSize();
Rect rect = Rect(0.0f, 0.0f, size.width, size.height);
return rect.containsPoint(locationInNode);
}
void Baby::setSprite()
{
if (_sprite)
_sprite->removeFromParent();
_sprite = Sprite::initWithFile(_crying ? "baby_crying.png" : "baby.png");
Vec2 size = getContentSize();
_sprite->setPosition(size.width * 0.5f, size.height * 0.5f);
addChild(_sprite);
}
You add the Baby node in the parent instead of the sprite, using:
_baby = Baby::create();
_baby->setPosition(Wherever);
addChild(_baby);
Where _baby is an instance variable and use the isInside() method to test if a touch event is within the bounds of the sprite and call its touched() method:
Touch *touch = ...;
if (_baby->isInside(touch)) {
_baby->touched();
}
and the Baby object will ignore the touch depending on state.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have programmed a simple top-down car driving game that resembles the first GTA, on the GameBoyAdvance. I have used only vector graphics for doing so, and the GBA doesn't handle it very well; basically with 5 pedestrian instances it lags.
I don't have much experience in optimizing code, so I would like to know if there are some tweaks I could make to my code in order to make it run faster, not depending on the fact that it runs on a GBA.
The collision testing I use is SAT (separating axis theorem) as I've found it to be the easisest one for collision check with vector graphics; the game is very simple itself.
Here is the code:
/*
GTA Vector City
Author: Alberto Taiuti
Version: 2.0
*/
#include "Global.h"
#include <string.h>
#include <cstdio>
#include "font.h"
#include "CVector2D.h"
#include "CCar.h"
#include "CPed.h"
#include <vector>
#include <memory>
/* GLOBAL VARIABLES */
void CheckCollisionsRect(CRect *test_a, CRect *test_b);
std::vector<CVector2D> PrepVectors(CRect *shape);
CVector2D GetMinMaxShape(std::vector<CVector2D> vect_shape, CVector2D axis);
void CheckCollisionRectVSPoint(CRect *test_a, CVector2D *point);
/* MAIN */
// The entry point for the game
int main()
{
// Frame counter
uint32_t frames = 0;
// Previous & current buttons states
static uint16_t prev_buttons = 0, cur_buttons = 0;
// Put the display into bitmap mode 3, and enable background 2.
REG_DISPCNT = MODE4 | BG2_ENABLE;
// Set up the palette.
SetPaletteBG(BLACK, RGB(0, 0, 0)); // black
SetPaletteBG(WHITE, RGB(31, 31, 31)); // white
SetPaletteBG(GREY, RGB(15, 15, 15)); // grey
SetPaletteBG(RED, RGB(31, 0, 0)); // red
SetPaletteBG(GREEN, RGB(0, 31, 0)); // green
SetPaletteBG(BLUE, RGB(0, 0, 31)); // blue
// Create car instance
CCar *car = new CCar(50,50);
// Create a building
/*CRect *test_b = new CRect(100.0f, 100.0f, 30, 30);
CRect *test_c = new CRect(120.0f, 120.0f, 30, 30);
CRect *test_d = new CRect(30.0f, 30.0f, 30, 30);*/
// Pedestrian instances
int ped_number = 10; // Number of pedestrians
std::vector<CPed*> peds; // Ped. entities container (made of smart pointers)
typedef std::vector<CPed*>::iterator p_itor; // Iterator
for(int i = 1; i <= ped_number; i++)
{
peds.push_back(new CPed(i, RED, 2.0f));
}
// Check whether the game is over
bool end = false;
// Main loop
while (!end)
{
// Flip the screen
FlipBuffers();
//Clear the screen
ClearScreen8(BLACK);
// Update frame counter
frames ++;
// Get the current state of the buttons.
cur_buttons = REG_KEYINPUT;
// Handle Input
car->HandleInput(prev_buttons, cur_buttons);
// Logic
car->Update();
for(int i = 0; i < ped_number; i++)
{
peds[i]->Update();
}
for(int i = 0; i < ped_number; i++)
{
CheckCollisionRectVSPoint(car->shape, peds[i]->pos);
}
/*CheckCollisionsRect(car->shape, test_b);
CheckCollisionsRect(car->shape, test_c);
CheckCollisionsRect(car->shape, test_d);
CheckCollisionRectVSPoint(car->shape, test_ped->pos);*/
// Render
car->Draw();
for(int i = 0; i < ped_number; i++)
{
peds[i]->Draw();
}
/*test_b->DrawFrame8(GREEN);
test_c->DrawFrame8(WHITE);
test_d->DrawFrame8(RED);
test_ped->Draw();*/
prev_buttons = cur_buttons;
// VSync
WaitVSync();
}
// Free memory
delete car;
//delete test_b; delete test_c; delete test_d;
//delete test_ped;
for(p_itor itor = peds.begin(); itor != peds.end(); itor ++)// Delete pedestrians
{
peds.erase(itor);
}
return 0;
}
void CheckCollisionsRect(CRect *test_a, CRect *test_b)
{
// If the two shapes are close enough, check for collision, otherways skip and save calculations to the CPU
//if((pow((test_a->points[0]->x - test_b->points[0]->x), 2) + pow((test_a->points[0]->y - test_b->points[0]->y), 2)) < 25.0f)
{
// Prepare the normals for both shapes
std::vector<CVector2D> normals_a = test_a->GetNormalsAsArray();
std::vector<CVector2D> normals_b = test_b->GetNormalsAsArray();
// Create two containers for holding the various vectors used for collision check
std::vector<CVector2D> vect_test_a = PrepVectors(test_a);
std::vector<CVector2D> vect_test_b = PrepVectors(test_b);
// Get the min and max vectors for each shape for each projection (needed for SAT)
CVector2D result_P1 = GetMinMaxShape(vect_test_a, normals_a[1]); //
CVector2D result_P2 = GetMinMaxShape(vect_test_b, normals_a[1]); //
// If the two objects are not colliding
if(result_P1.y < result_P2.x || result_P2.y < result_P1.x)
{
return;
}
CVector2D result_Q1 = GetMinMaxShape(vect_test_a, normals_a[0]); // First axis couple
CVector2D result_Q2 = GetMinMaxShape(vect_test_b, normals_a[0]); //
if(result_Q1.y < result_Q2.x || result_Q2.y < result_Q1.x)
{
return;
}
CVector2D result_R1 = GetMinMaxShape(vect_test_a, normals_b[1]); //
CVector2D result_R2 = GetMinMaxShape(vect_test_b, normals_b[1]); //
if(result_R1.y < result_R2.x || result_R2.y < result_R1.x)
{
return;
}
CVector2D result_S1 = GetMinMaxShape(vect_test_a, normals_b[0]); // Second axis couple
CVector2D result_S2 = GetMinMaxShape(vect_test_b, normals_b[0]); //
if(result_S1.y < result_S2.x || result_S2.y < result_S1.x)
{
return;
}
// Do something
PlotPixel8(200, 10, WHITE);
PlotPixel8(200, 11, WHITE);
PlotPixel8(200, 12, WHITE);
}
}
// Check for collision between an OOBB and a point
void CheckCollisionRectVSPoint(CRect *test_a, CVector2D *point)
{
// Prepare the normals for the shape
std::vector<CVector2D> normals_a = test_a->GetNormalsAsArray();
// Create a container for holding the various vectors used for collision check
std::vector<CVector2D> vect_test_a = PrepVectors(test_a);
// Get projections for the OOBB (needed for SAT)
CVector2D result_P1 = GetMinMaxShape(vect_test_a, normals_a[1]);
float result_point = point->DotProduct(normals_a[1]);
// If the two objects are not colliding on this axis
if(result_P1.y < result_point || result_point < result_P1.x)
{
return;
}
CVector2D result_Q1 = GetMinMaxShape(vect_test_a, normals_a[0]);
result_point = point->DotProduct(normals_a[0]);
// If the two objects are not colliding on this axis
if(result_Q1.y < result_point || result_point < result_Q1.x)
{
return;
}
// Do something
PlotPixel8(200, 10, WHITE);
PlotPixel8(200, 11, WHITE);
PlotPixel8(200, 12, WHITE);
}
// Returns a container with projection vectors for a given shape
std::vector<CVector2D> PrepVectors(CRect *shape)
{
std::vector<CVector2D> vect;
// Create vectors for projection and load them into the arrays
for( uint16_t i=0; i < 5; i++)
{
// Get global position of vectors and then add them to the array
vect.push_back(shape->GetVectorGlobal(i));
}
return vect;
}
CVector2D GetMinMaxShape(std::vector<CVector2D> vect_shape, CVector2D axis)
{
// Set initial minimum and maximum for shape's projection vectors
float min_proj = vect_shape[1].DotProduct(axis);
float max_proj = vect_shape[1].DotProduct(axis);
// Calculate max and min projection vectors by iterating along all of the corners
for(uint16_t i = 2; i < vect_shape.size(); i ++)
{
float current_proj = vect_shape[i].DotProduct(axis);
// Select minimum projection on axis
if(current_proj < min_proj) // If current projection is smaller than the minimum one
min_proj = current_proj;
// Select maximum projection on axis
if(current_proj > max_proj) // If current projection is greater than the minimum one
max_proj = current_proj;
}
return (CVector2D(min_proj, max_proj)); // Return a vector2D as it is a handy way for returning a couple of values
}
Many thanks in advance to everyone and sorry for the messy code!
I gave it a really quick reading so I may have overlooked something. Well, there are obvious tips for improving performance such as passing vectors to functions by reference. Using prefix incrementation instead of postfix is also a good habit. These two rules are definitely nothing like 'premature optimization the, root of ...'. Do not delete pedestrians one by one but use std::vector::clear(). And If you claim you use smart pointers, you shoud, because it seems you have memory leak because you did not delete the pedestrian pointers. And use const keyword whereever possible. Once you make the obvious correction, and the speed is still not satisfactory, then you need to use profiler.
And read something about optimization, here for example: http://www.agner.org/optimize/optimizing_cpp.pdf
One thing leaps out at me (apart from the continuous passing of vectors by value rather than reference, which will be incredibly costly!)
In you collision detection, you're seeing if the car hits each pedestrian
for(int i = 0; i < ped_number; i++)
{
CheckCollisionRectVSPoint(car->shape, peds[i]->pos);
}
Then, in the collision detector, you're repeating a lot of the same processing on the car shape every time:-
// Prepare the normals for both shapes
std::vector<CVector2D> normals_a = test_a->GetNormalsAsArray();
// Create two containers for holding the various vectors used for collision check
std::vector<CVector2D> vect_test_a = PrepVectors(test_a);
.. etc...
You should rework that loop to create the normals etc for the car just once, and then reuse the results for each check against a pedestrian.