i'm developing freehand drawing game, and the drawing isn't very smooth , i used this code:
void Canvas::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event) {
Canvas::drawEvenlySpacedSprites(touch->getLocation(),touch->getPreviousLocation());
}
void Canvas::drawEvenlySpacedSprites(Vec2 start, Vec2 end) {
// begin drawing to the render texture
_target->begin();
float distance = start.getDistance(end);
if (distance > 1) {
int d = (int)distance;
for (int i = 0; i < d; i++)
{
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float)i / distance;
Sprite * sprite = Sprite::create("brush3.png");
sprite->setColor(Color3B::BLUE);
sprite->setPosition(Vec2(start.x + (difx * delta), start.y + (dify * delta)));
sprite->visit();
}
}
// finish drawing and return context back to the screen
_target->end();
}
I'm working with cocos2d-x V3.3
Please help me to get smooth drawing
Thanks
As Nadarian says, you should not create a new Sprite each time you draw it.
Even though you cannot reuse same sprite, you can create sprite from same SpriteFrame.
First, you need to store SpriteFrame on some variable. Or, you can use SpriteFrameCache if you want.
I will show the code which will use SpriteFrame.
// create sprite
auto sprite = cocos2d::Sprite::create("brush3.png");
// get spriteframe from sprite
auto spriteframe = sprite->getSpriteFrame();
and on Canvas::drawEvenlySpacedSprites, create Sprite from the stored spriteframe.
Here is the example, change the this code
Sprite * sprite = Sprite::create("brush3.png");
to this code,
Sprite * sprite = Sprite::createWithSpriteFrame(spriteframe);
I've found this implementation, I hope it will helps you: http://build-failed.blogspot.it/2012/08/freehand-drawing-with-cocos2d-x-and.html
Related
I create a little game on cocos2d-x and have some problem in mobile version. Game have layer with terrain and character and layer with ui/info objects. Layer with terrain does not move. And layer with ui/info move with character (so it static on screen).
In mobile version all sprites from ui layer are trembling, but only sprites, labels are static. In PC version sprites and labels are also static.
Create label and sprite. Label static on PC (Win and Mac) and mobile (Android), sprite static on PC and tremble on mobile:
auto infoLayer = m_params->getGameInfoDelegate(); // class GameInfo
auto size = Director::getInstance()->getVisibleSize();
TTFConfig ttfconfig("fonts/Marker Felt.ttf", 100);
auto label = Label::createWithTTF(ttfconfig, "0");
label->setPosition(Vec2(size.width / 2, size.height / 2 + 40));
label->setString("Hello");
infoLayer->getLayer()->addChild(label, 10);
auto spr = Sprite::create();
spr->setColor(Color3B(200, 100, 100));
spr->setTextureRect(Rect(0, 0, 150, 150));
spr->setPosition(Vec2(size.width / 2, size.height / 2 - 40));
infoLayer->getLayer()->addChild(spr, 9);
Update position layer and camera:
update(float t)
{
...
m_cameraFollow->update();
...
}
void CameraFollow::update()
{
float moveX;
float moveY;
...
m_camera->move(Vec2(moveX, moveY)); // class GameCamera
}
void GameCamera::move(const cocos2d::Vec2& m)
{
float x;
float y;
...
m_position.x = x;
m_position.y = y;
m_camera->setPosition(m_position); // class cocos2d::Camera
auto infoPanel = m_params->getGameInfoDelegate(); // class GameInfo
if(infoPanel)
{
infoPanel->setMoving(m_position - m_startPosition);
}
}
class GameInfo : public cocos2d::Layer, public GameInfoDelegate
void GameInfo::setMoving(const cocos2d::Vec2 &position)
{
this->setPosition(position);
}
So, how i can fix it?
The answer to your question is complicated. The main reason is that your phone does not have the same processing power as your computer, and Cocos2d-x uses some clever optimizations to try and hide that. With moving sprites, it has to redraw them every frame (usually 30-60 fps), and slight inconsistencies can lead to this effect.
To remedy this, I would double check that your fps is 60, because 30 fps will lead to trembling. Also, if you're updating the sprite's position in update(float dt), I would try and use the physics engine instead, with velocities. If that isn't an option, maybe try to have less layers, because the more sprites you draw ontop of one another, the more it will look like it is jittering. Let me know if any of these solutions work.
The issue may be related to how you are moving the camera. Setting new X,Y coordinates through your update method without factoring in the delta time on each update call will result in jerky movement on screen.
You need to smooth out the movement from one location to another.
Try this:
update(float dt)
{
...
m_cameraFollow->update(dt);
...
}
void CameraFollow::update(float dt)
{
float moveX;
float moveY;
float speed = 1.0f;
...
Vec2 cameraPosition = m_camera->getPosition();
Vec2 targetPosition = Vec2(moveX, moveY);
Vec2 newPosition = cameraPosition.lerp(targetPosition, dt * speed);
m_camera->move(newPosition);
}
setTouchEnabled(true);
ball=CCSprite::create("soccer_ball.png");
ball->setPosition(ccp(100,100));
addChild(ball,1);
//CREATE WORLD
b2Vec2 gravity(0, -9.8); //normal earth gravity, 9.8 m/s/s straight down!
bool doSleep = true;
myWorld = new b2World(gravity);
myWorld->SetAllowSleeping(doSleep);
//BODY DEFINITION
myBodyDef.type = b2_dynamicBody; //this will be a dynamic body
myBodyDef.position.Set(0, 20); //set the starting position
myBodyDef.angle = 0; //set the starting angle return true;
myBodyDef.userData=ball;
//CREATE SHAPE
b2CircleShape ballShape;
ballShape.m_p.Set(2.0f,3.0f);
ballShape.m_radius=50.0/PTM_RATIO;
//CREATE BODY
dynamicBody = myWorld->CreateBody(&myBodyDef);
//FIXTURE DEFINITION
b2FixtureDef ballFixtureDef;
ballFixtureDef.shape = &ballShape;
ballFixtureDef.density = 1;
dynamicBody->CreateFixture(&ballFixtureDef);
}
void HelloWorld::update()
{
float32 timeStep = 1/20.0; //the length of time passed to simulate (seconds)
int32 velocityIterations = 8; //how strongly to correct velocity
int32 positionIterations = 3; //how strongly to correct position
myWorld->Step( timeStep, velocityIterations, positionIterations);
}
I am learning box2d basic concepts and i have put my code here. I created a box2d circle shape. now i want to add sprite of ball in that circle shape. I have used myBodyDef.userData=ball; but it is not working .. i used gles-render code to debug draw but in that circle body is different and ball sprite is different. when i applyforce or impluse body works perfectly but dont attached to ball sprite..is any mistake in my code. I want to apply force to ball and ball should bounce according to physics but i can not attach to body plz help me.
Body and sprite looks like this
Here's my version working for V-3.6:
void GamePlayScreen::update(float dt) {
phyWorld->Step(dt, 8, 1);
// Iterate over the bodies in the physics phyWorld
for (b2Body* b = phyWorld->GetBodyList(); b; b = b->GetNext()) {
if (b->GetUserData() != NULL) {
// Synchronize the AtlasSprites position and rotation with the corresponding body
Sprite* sprActor = (Sprite*) b->GetUserData();
sprActor->setPosition(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
sprActor->setRotation(-1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));
}
}
}
Well, my question is simple, I'm starting with cocos2d-x programming and I'm trying to make a tiled infinite background, lets say I have a png of an image I want to repeat infinitely as a background, What I did was that I created a Sprite as a container, then I added sprites with the same image, one aside the next, covering about 110% the screen, then, following the Space game tutorial I created the same sprite of sprites once again as a second background and I use it as two large identical images to scroll infinite... it works perfect... but I'm wondering if it is possible to create the second sprite by just copying the frist. Some code maybe will clear my situation:
this goes in the init():
acum = 0.0;
city1 = CCSprite::create();
for(int i = 0; i < 12; ++i) {
CCSprite *city = CCSprite::createWithSpriteFrameName("env_buildings_background2.png");
city->setPosition(ccp(acum, 0));
city1->addChild(city);
acum+= city->getContentSize().width*0.99;
}
city1->setContentSize(ccp(acum, 0));
city2 = CCSprite::create();
acum = 0.0;
for(int i = 0; i < 12; ++i) {
CCSprite *city = CCSprite::createWithSpriteFrameName("env_buildings_background2.png");
city->setPosition(ccp(acum, 0));
city2->addChild(city);
acum+= city->getContentSize().width*0.99;
}
city2->setContentSize(ccp(acum, 0));
_backgroundNode->addChild(city1, -1 , buildingspeed, ccp(0, winSize.height * 0.6));
_backgroundNode->addChild(city2, -1 , buildingspeed, ccp(city1->getContentSize().width, winSize.height * 0.6));
printf("%f - %f\n", city1->getContentSize().width, city2->getContentSize().width);
The main problem is here, where I need to create city2 from city1 and not just repeat code... is there a way to do this? I don't see a constructor in CCSprite that allows me to do so...
this goes in the update():
CCArray *cities = CCArray::createWithCapacity(2);
cities->addObject(city1);
cities->addObject(city2);
for ( int ii = 0; ii <cities->count(); ii++ ) {
CCSprite * city = (CCSprite *)(cities->objectAtIndex(ii));
float xPosition = _backgroundNode->convertToWorldSpace(city->getPosition()).x;
float size = city->getContentSize().width;
if ( xPosition < -size ) {
_backgroundNode->incrementOffset(ccp(city->getContentSize().width*2,0),city);
}
}
I will appreciate any help, thanks in advance.
This code will create the exact snapshot of your citySprite. And as this is a snapshot, you'll be having no access to the sprites that it contains. Cause they are now embedded in the image or sprite itself.
float citySpriteWidth = 480;
float citySpriteHeight = 320;
//Set position in order to make it fit inside CCRenderTexture (You can change this later)
citySprite->setPosition(ccp(citySpriteWidth/2, citySpriteHeight/2));
CCRenderTexture *render = CCRenderTexture::renderTextureWithWidthAndHeight(citySpriteWidth, citySpriteWidth);
render->beginWithClear(0, 0, 0, 0);
citySprite->visit();
render->end();
CCTexture2D *tex = render->getSprite()->getTexture();
CCSprite *newCitySprite = CCSprite::spriteWithTexture(tex);
newCitySprite->setFlipY(true); //Texture might be upside down
Hope this helps your requirements.
Else if your problem is just code repetition. Then you can write a function which would give you your city sprite containing other sprites like this one. And call it number of times you want it.
CCSprite* creteCitySprite()
{
float acum = 0.0;
CCSprite *city1 = CCSprite::create();
for(int i = 0; i < 12; ++i)
{
CCSprite *city = CCSprite::createWithSpriteFrameName("env_buildings_background2.png");
city->setPosition(ccp(acum, 0));
city1->addChild(city);
acum+= city->getContentSize().width*0.99;
}
city1->setContentSize(CCSizeMake(acum, 0));
return city1;
}
EDITED:
This gives you what you want. But what I wanted to say is, the sub sprites which you added to your city1 will remain static.
CCSptire *getSpriteFromSprite(CCSprite *citySprite, float citySpriteWidth, float citySpriteHeight)
{
CCPoint prevPosition = citySprite->getPosition();
//Set position in order to make it fit inside CCRenderTexture (You can change this later)
citySprite->setPosition(ccp(citySpriteWidth/2, citySpriteHeight/2));
CCRenderTexture *render = CCRenderTexture::renderTextureWithWidthAndHeight(citySpriteWidth, citySpriteWidth);
render->beginWithClear(0, 0, 0, 0);
citySprite->visit();
render->end();
citySprite->setPosition(prevPosition);
CCTexture2D *tex = render->getSprite()->getTexture();
CCSprite *newCitySprite = CCSprite::spriteWithTexture(tex);
newCitySprite->setFlipY(true); //Texture might be upside down
}
As I am newbie to cocoa2d I am struggling alot to rotate the physics or dynamic body along an arc path.
The way I tried is as follows:
#define COS_ANIMATOR(position, timeCount, speed, waveMagnitude) ((cosf(timeCount * speed) * waveMagnitude) + position)
#define SIN_ANIMATOR(position, timeCount, speed, waveMagnitude) ((sinf(timeCount * speed) * waveMagnitude) + position)
CCSpriteBatchNode *pipe_parent = [CCSpriteBatchNode batchNodeWithFile:#"pipe.png" capacity:100];
CCTexture2D *pipeSpriteTexture_ = [pipe_parent texture];
PhysicsSprite *pipeSprite = [PhysicsSprite spriteWithTexture:pipeSpriteTexture_ rect:CGRectMake(0 ,0 ,55,122)];
//pipe = [CCSprite spriteWithFile:#"pipe.png"
// rect:CGRectMake(0, 0, 55, 122)];
[self addChild:pipeSprite];
// pipe.position = ccp(s.width/2 , 420.0);
b2BodyDef myBodyDef;
myBodyDef.type = b2_staticBody; //this will be a dynamic body
myBodyDef.position.Set(((s.width/2) - 90)/PTM_RATIO, 420.0/PTM_RATIO); //set the starting position
myBodyDef.angle = 0; //set the starting angle
b2Body* staticBody = world->CreateBody(&myBodyDef);
b2PolygonShape boxShape;
boxShape.SetAsBox(1,1);
b2FixtureDef boxFixtureDef;
boxFixtureDef.shape = &boxShape;
boxFixtureDef.density = 1;
boxFixtureDef.userData = pipeSprite;
boxFixtureDef.filter.groupIndex = -1;
staticBody->CreateFixture(&boxFixtureDef);
[pipeSprite setPhysicsBody:staticBody];
-(void) draw
{
//
// IMPORTANT:
// This is only for debug purposes
// It is recommend to disable it
//
[super draw];
const CGPoint newSpritePosition = ccp(COS_ANIMATOR(150, mTimeCounter, 0.05,50), SIN_ANIMATOR(400, mTimeCounter, -0.05, 50));
pipeSprite.position = newSpritePosition;
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
}
on following this approach my sprite rotating in circular motion instead of rotating in an arc path.
Please give your ideas or suggestions.
Thanks all
I'm not entirely sure what it is you are looking to accomplish when you talk about rotating in an arc path. I only see you setting a position, not a rotation, so are you just wanting to set a position, or a rotation, or both? Your position code looks like you are trying to achieve a circular (or elliptical) path because you are using the sine and cosine in the x,y position.
If you're looking to move a sprite along a sine curve, I did that today and it took a bit of trial and error. I had some variables for the amplitude and period, and from there I traced out a nice sine curve movement in the sprite's update: method.
CGPoint initialPosition; // set this to the sprite's initial position
float amplitude; // in points
float period; // in points
float y, x = initialPosition.x;
-(void) update:(ccTime)dt
{
x += dt * 100; // speed of movement across the screen. Picked by trial and error.
y = initalPosition.y + amplitude * sinf((x - initialPosition.x)/period);
sprite.position = ccp(x,y);
sprite.rotation = cosf((x - initialPosition.x)/period); // optional if you want to rotate along the path as well
}
Don't know if this is anything you are looking for but it might give you some ideas.
I have created an atlas with all images I will use in a class. If this was a sprite created from an image, I would create it like
mySprite = [CCSprite spriteWithFile:#"white.png" rect:frame];
"white.png" is a 1x1 pixel image that I am stretching to cover the entire CCSprite size, that is defined by rect:frame on that API.
But in order to optimize I/O and memory, I put white.png in an atlas and my idea was to create it using
mySprite = [CCSprite spriteWithSpriteFrameName:#"white.png"];
but this will create a 1x1 pixel sprite. So, my idea was to create a category to extend CCSprite with these lines
#implementation CCSprite (CCSprite_Resize)
-(void)resizeTo:(CGSize) theSize
{
CGFloat newWidth = theSize.width;
CGFloat newHeight = theSize.height;
float startWidth = self.contentSize.width;
float startHeight = self.contentSize.height;
float newScaleX = newWidth/startWidth;
float newScaleY = newHeight/startHeight;
self.scaleX = newScaleX;
self.scaleY = newScaleY;
}
so I could do this
mySprite = [CCSprite spriteWithSpriteFrameName:#"white.png"];
[mySprite resizeTo:frame.size];
and the 1x1 sprite would be stretched to cover the size I want.
The problem is that this is not working.
any clues? thanks.
Make shore you are not overriding somthing like - (CGAffineTransform)nodeToParentTransform. I'm using Box2d physics with cocos2d, and provided be template class PhysicsSprite (subclass of CCSprite) overrided it, and there was a bug: scale property didn't change anything. I fixed it like this:
- (CGAffineTransform)nodeToParentTransform
{
b2Vec2 pos = body_->GetPosition();
float x = pos.x * PTM_RATIO;
float y = pos.y * PTM_RATIO;
// Make matrix
float radians = body_->GetAngle();
float c = cosf(radians);
float s = sinf(radians);
if (!CGPointEqualToPoint(anchorPointInPoints_, CGPointZero))
{
x += c * -anchorPointInPoints_.x * scaleX_ + -s * -anchorPointInPoints_.y * scaleY_;
y += s * -anchorPointInPoints_.x * scaleX_ + c * -anchorPointInPoints_.y * scaleY_;
}
// Rot, Translate Matrix
transform_ = CGAffineTransformMake( c * scaleX_, s * scaleX_,
-s * scaleY_, c * scaleY_,
x, y );
return transform_;
}
In original, there was no scaleX_ and scaleY_ multiplying.
It seems that in your case you can use CCLayerColor to create one-color layer. There is no need to use sprite for it.
About your question - make sure, that frame.size is not zero (CGSizeZero)