Hi, I’ve been having a lot of trouble implementing a smooth and reliable touch-and-hold solution for firing bullet (sprites) - even after looking through this forum and Stack Overflow for help.
The solution has to switch between touch-began, touch moved and touch ended seamlessly: always firing bullets at the touch location until the finger is released. At the moment I have many problems with reliability and stability with every case but touchmoved, which works fine.
The exact issue is around half the time a finger is held down (touchBegan + scheduler) the bullets appear but disappear a second later but other times they move towards the touch perfectly - something is deleting them and i don’t have much experience with schedulers or actions to know what.
Heres the code, of special importance is the scheduling at the bottom of init(), the touch methods, and the 2 bullet firing methods. Or if you’ve got your own implementation, equally useful. Thanks in advance!
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
visibleSize = CCDirector::sharedDirector()->getVisibleSize();
visibleOrigin = CCDirector::sharedDirector()->getVisibleOrigin();
player = CCSprite::create("player.png");//, CCRectMake(0, 0, 64, 128)); 2 argument constructor optional
CCPoint playerOrigin = ccp(160, 320); //160,140
player->setPosition(playerOrigin);
this->addChild(player, 1);
screenHeld = false;
gun = CCSprite::create("gun.png");
this->addChild(gun, 2);
firePoint = ccp(gun->getPosition().x + 13, gun->getPosition().y);
cursor = CCSprite::create("cursor.png");
this->addChild(cursor);
pLabel = CCLabelTTF::create("", "Arial", 24);
pLabel->setPosition(ccp(visibleSize.width/2,visibleSize.height-100));
// add the label as a child to this layer
this->addChild(pLabel, 1);
this->setTouchEnabled(true);
this->scheduleUpdate(); //game logic every frame
this->schedule(schedule_selector(HelloWorld::fireBullets), 0.05);
return true;
}
void HelloWorld::update(float dt)
{
gun->setPosition(ccp(player->getPosition().x+27, player->getPosition().y + 8));
}
void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{
CCTouch* touch = (CCTouch*)( pTouches->anyObject() ); // get single-touch as opposed to multitouch
touchLocation = CCDirector::sharedDirector()->convertToGL(touch->getLocationInView());
if (touchLocation.x <= 400 && !jumping && onGround)
{
jumping = true;
jumpStartTime = getTimeTick();
}
if (touchLocation.x > 400)
{
float dX = touchLocation.x - gun->getPosition().x;
float dY = touchLocation.y - gun->getPosition().y;
touchAngle = atan2(dY, dX);
gun->setRotation(-CC_RADIANS_TO_DEGREES(touchAngle));
cursor->setPosition(touchLocation);
screenHeld = true;
//fireBullet(touchAngle);
}
}
void HelloWorld::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent)
{
CCTouch* touch = (CCTouch*)( pTouches->anyObject() ); // for single-touch as opposed to multitouch
touchLocation = CCDirector::sharedDirector()->convertToGL(touch->getLocationInView());
//CCRect lever1Box = lever1->boundingBox();
//if(lever1Box.containsPoint(location)) ...
if (touchLocation.x > 400)
{
float dX = touchLocation.x - gun->getPosition().x;
float dY = touchLocation.y - gun->getPosition().y;
float angle = atan2(dY, dX);
gun->setRotation(-CC_RADIANS_TO_DEGREES(angle));
cursor->setPosition(touchLocation);
screenHeld = false; //not technically true but touchMoved bullet firing works differently (not scheduled, every movement instead)
if (getTimeTick() - lastBulletFire > 50)
{
fireBullet(angle);
}
}
}
void HelloWorld::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
{
screenHeld = false;
//note: this is not the problem because I removed it and problem remained
//as in, I removed screenHeld requirement and bullet firing was still erratic
}
//this is for touchMoved and works fine, everytime finger is moved bullet fired
void HelloWorld::fireBullet(float angle)
{
CCSprite* bullet = CCSprite::create("bullet.png");
bullet->setPosition(ccp(gun->getPosition().x, gun->getPosition().y)); //add a random spread to the y value (or maybe the y-value of the destination)
this->addChild(bullet, 5);
bullet->setRotation(-CC_RADIANS_TO_DEGREES(angle));
bullet->runAction(CCSequence::create(CCMoveBy::create(1.5f, ccp(800 * cos(angle), 800 * sin(angle))), NULL));
lastBulletFire = getTimeTick();
}
//this is for touchBegan and has issues, scheduled to run every 50ms touch-held
void HelloWorld::fireBullets(CCTime dt)
{
if (screenHeld)
{
CCSprite* bullet = CCSprite::create("bullet.png");
bullet->setPosition(ccp(gun->getPosition().x, gun->getPosition().y));
bullet->setRotation(-CC_RADIANS_TO_DEGREES(touchAngle));
bullet->runAction(CCSequence::create(CCMoveBy::create(1.5f, ccp(800 * cos(touchAngle), 800 * sin(touchAngle))), NULL));
this->addChild(bullet, 5);
}
}
float HelloWorld::getTimeTick() {
timeval time;
gettimeofday(&time, NULL);
unsigned long millisecs = (time.tv_sec * 1000) + (time.tv_usec/1000);
return (float) millisecs;
}