OK so i have a character that moves with the mouse. I need it to stay in the center of the screen(kind of like a platformer game). I can't figure out how to access the camera and move it. (Note: I have tried Vcam and moving all of the other objects but Vcam makes the file slow or something [or so i have heard] and moving the other objects in kind of like cheating [and for my needs is insufficient]) I don't have any code because i don't know where to start. Maybe someone can point me into the right direction.
Thanks,
Thor
One way is to store everyhting in one DisplayObject and then move that single object based on the camera movement. Instead of moving the camera, move the main container the opposite direction of the camera. I'm not sure why you seem to suggest a strategy like this is "cheating" as it is a perfectly suitable way to doing this.
This is my previous answer on a similar question found here.
What I do here is:
Create a Map class with a property camera which is another custom class MapCamera.
The MapCamera has five properties:
_x
_y
map - a reference to the instance of Map owning this MapCamera
offsetX
offsetY
The offset values represent the x and y spacing from the left and top edges of the screen, which should be set to half of the stage width and height so that the camera will centre on the stage correctly.
The _x and _y properties are private, and have getters and setters.
The getters are pretty basic:
public function get x():Number{ return _x; }
public function get y():Number{ return _y; }
The setters are where the viewport will be altered, like so:
public function set x(n:Number):void
{
_x = n;
map.x = -(_x + offsetX);
}
public function set y(n:Number):void
{
_y = n;
map.y = -(_y + offsetY);
}
From here, you add your children into the Map container and then can simply go:
map.camera.x = player.x;
map.camera.y = player.y;
Which will cause the player to always be in the centre of the screen.
Your camera is only a vector that modifies position of all renderable objects.
myMovieClip.x = movingClipPosition.x + camera.x
So if the camera.x is moved to the right, this will make the object move the left, giving the impression of a "camera".
Related
The past few days I've been trying to figure out a display bug I don't understand. I've been working on a simple 2d platformer with box2d and orthogonal Tiled maps. So far so good, the physics work and using the b2d debug renderer I can assert proper player fixture and camera movement through the level.
Now next step I've tried to load textures to display sprites instead of debug shapes. This is where I stumble. I can load animations for my player body/fixture, but when I use the setCenter() method to center the texture on the fixture it is always out of center.
I've tried approaches via halving texture witdths and heights hoping to center the texture on the player fixture but I get the exact same off position rendering. I've played aorund with world/camera/screen unit coordinates but the misalignement persists.
I'm creating the player in my Player class with the following code.
First I define the player in box2d:
//define player's physical behaviour
public void definePlayer() {
//definitions to later use in a body
BodyDef bdef = new BodyDef();
bdef.position.set(120 / Constants.PPM, 60 / Constants.PPM);
bdef.type = BodyDef.BodyType.DynamicBody;
b2body = world.createBody(bdef);
//Define needed components of the player's main fixture
FixtureDef fdef = new FixtureDef();
PolygonShape shape = new PolygonShape();
shape.setAsBox(8 / Constants.PPM, 16 / Constants.PPM); //size of the player hitbox
//set the player's category bit
fdef.filter.categoryBits = Constants.PLAYER_BIT;
//set which category bits the player should collide with. If not mentioned here, no collision occurrs
fdef.filter.maskBits = Constants.GROUND_BIT |
Constants.GEM_BIT |
Constants.BRICK_BIT |
Constants.OBJECT_BIT |
Constants.ENEMY_BIT |
Constants.TREASURE_CHEST_BIT |
Constants.ENEMY_HEAD_BIT |
Constants.ITEM_BIT;
fdef.shape = shape;
b2body.createFixture(fdef).setUserData(this);
}
Then I call the texture Region to be drawn in the Player class constructor:
//define in box2d
definePlayer();
//set initial values for the player's location, width and height, initial animation.
setBounds(0, 0, 64 / Constants.PPM, 64 / Constants.PPM);
setRegion(playerStand.getKeyFrame(stateTimer, true));
And finally, I update() my player:
public void update(float delta) {
//center position of the sprite on its body
// setPosition(b2body.getPosition().x - getWidth() / 2, b2body.getPosition().y - getHeight() / 2);
setCenter(b2body.getPosition().x, b2body.getPosition().y);
setRegion(getFrame(delta));
//set all the boolean flags during update cycles approprietly. DO NOT manipulate b2bodies
//while the simulation happens! therefore, only set flags there, and call the appropriate
//methods outside the simulation step during update
checkForPitfall();
checkIfAttacking();
}
And my result is
this, facing right
and this, facing left
Update:
I've been trying to just run
setCenter(b2body.getPosition().x, b2body.getPosition().y);
as suggested, and I got the following result:
facing right and facing left.
The sprite texture flip code is as follows:
if((b2body.getLinearVelocity().x < 0 || !runningRight) && !region.isFlipX()) {
region.flip(true, false);
runningRight = false;
} else if ((b2body.getLinearVelocity().x > 0 || runningRight) && region.isFlipX()) {
region.flip(true, false);
runningRight = true;
}
I'm testing if either the boolean flag for facing right is set or the x-axis velocity of my player b2body has a positive/negative value and if my texture region is already flipped or not and then use libGDX's flip() accordingly. I should not be messing with fixture coords anywhere here, hence my confusion.
The coordinates of box2d fixtures are offsets from the position, the position isn't necessarily the center (although it could be depending on your shape definition offsets). So in your case i think the position is actually the lower left point of the box2d polygon shape.
In which case you don't need to adjust for width and height because sprites are also drawn from bottom left position. So all you need is ;
setPosition(b2body.getPosition().x , b2body.getPosition().y );
I'm guessing you flip the box2d body when the player looks left the position of the shape is now bottom right so the sprite offset of width/2 and height/2 is from the bottom right instead. So specifically when you are looking left you need an offset of
setPosition(b2body.getPosition().x - getWidth() , b2body.getPosition().y );
I think looking right will be fixed from this, but i don't know for sure how you handle looking left in terms of what you do to the body, but something is done because the offset changes entirely as shown in your capture. If you aren't doing some flipping you could add how you handle looking right to the question.
EDIT
It seems the answer was that the sprite wasn't centered in the sprite sheet and this additional space around the sprite caused the visual impression of being in the wrong place (see comments).
I'm trying to make a game that will take what users draw on their screens and create a sprite with a physics body. I looked around and I saw a tutorial that demonstrated this. Unfortunately the tutorial was made in Cocos2d-x v2.
http://build-failed.blogspot.com/2012/08/freehand-drawing-with-cocos2d-x-and.html
That is the tutorial that I am referring to.
http://www.cocos2d-x.org/wiki/Render_To_Texture
I tried to use that cocos2d-x tutorial to help me, but it has been labeled outdated (I tried it anyway and it didn't work). Is it still possible to allow the user to draw through this method? Or do I need to find another method to allow the user to draw the sprites? Any suggestions would be helpful. Thanks.
void GameScene::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event)
{
Point start = touch->getLocation();
start = Director::getInstance()->convertToGL(start);
Point end = touch->getPreviousLocation();
end = Director::getInstance()->convertToGL(end);
target->begin();
float distance = start.getDistance(end);
for (int i = 0; i < distance; i++)
{
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float) i / distance;
brush->setPosition(Point(start.x + (difx * delta), start.y + (dify * delta)));
brush->visit();
}
target->end();
}
void GameScene::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event)
{
myObjectSprite = Sprite::createWithTexture(target->getSprite()->getTexture());
myObjectSprite->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2));
this->addChild(myObjectSprite);
}
This is what I gathered from those links. I can draw but there are a few problems.
The first attempt to draw always reflects horizontally over the center of the screen.
After a few drawings the fps begins to drop, and the app begins to take up a lot of memory and CPU usage.
The for loop in the onTouchMoved method is used because the onTouchEnded method isn't called fast enough, so there is often a large gap between each point that the onTouchMethod obtains. The loop is used to draw the brush sprites in a line between each point to prevent the gaps. However, for some reason it is not drawing in between the gaps.
Additionally, in the first link the person uses b2PolygonShape and b2FixtureDef from box2d. What are the new names of the classes in cocos2d-x v3?
I am working on a game. I constructed my player as here: (I am using a gravity on my world)
private ArrayMap<String, GameObject.Constructor> constructors = new ArrayMap<String, GameObject.Constructor>(String.class, GameObject.Constructor.class);
private ArrayList<GameObject> instances = new ArrayList<GameObject>();
assets.load("hand.obj", Model.class);
...
model = assets.get("hand.obj", Model.class);
constructors.put("hand", new GameObject.Constructor(model, new btBoxShape(new Vector3(2.5f, 7.5f, 2.5f)), 1f));
...
hand = constructors.get("hand").construct(); // that construct method returns me model, shape and constructions.. the GameObject extends ModelInstance, so i can use it like a modelinstance
hand.transform.setToTranslation(x, y, z);
hand.body.proceedToTransform(hand.transform);
hand.body.setUserValue(instances.size());
hand.body.setCollisionFlags(hand.body.getCollisionFlags()| btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
world.addRigidBody(hand.body);
hand.body.setContactCallbackFlag(OBJECT_FLAG);
hand.body.setContactCallbackFilter(OBJECT_FLAG);
Then, in render method I am moving it:
if (!hand.body.isActive()) hand.body.activate();
if (Gdx.input.isKeyPressed(Keys.W)){
hand.body.translate(new Vector3(0,0,-1));
}
else if (Gdx.input.isKeyPressed(Keys.S)) {
hand.body.translate(new Vector3(0,0,+1));
}
That's nice! The moving now works good, when I am moving at the flat ground. Whenever there is an object before me, it is not as expected. Because my player shape is biger than
object shape (which is 2.5f, 2.5f, 2.5f), it kind of falls on it. So I would like to set the rotation to be still the same, so the object will not be rotating (so it will not "fall" on the object before). And so I tried to do it, and I failed. Because there are functions like rotate, and I want to something like setRotation
. And so, there is a setToRotation, but you can not pass there a Quaternion.
I need help. I tried to use a btKinematicCharacterController but it was bad. The ghostObject every time falled through object, but the objects got a collision from him.
and so I want to create a player movment, like in games like Wow, minecraft, and so on.
I looked at the btKinematicCharacterController again. The reason why my ghostobject falled through the ground was. Generally, I don't know the reason: D probably I was using another broadphase for ghost, that for world. This line fixes it: characterController.setUseGhostSweepTest(false);
and I am getting another problem, when I am walking on my ground (a lot of objects), the character is getting to lesser Y position. I don't know why.
Here is my construction:
btPairCachingGhostObject ghostObject;
btConvexShape ghostShape;
btKinematicCharacterController characterController;
Vector3 characterDirection = new Vector3();
Vector3 walkDirection = new Vector3();
...
ghostObject = new btPairCachingGhostObject();
ghostObject.setWorldTransform(hand.transform);
ghostShape = new btCapsuleShape(5f, 0.5f);
ghostObject.setCollisionShape(ghostShape);
ghostObject.setCollisionFlags(btCollisionObject.CollisionFlags.CF_CHARACTER_OBJECT);
characterController = new btKinematicCharacterController(ghostObject, ghostShape, .00001f);
// And add it to the physics world
characterController.setUseGhostSweepTest(false);
world.addCollisionObject(ghostObject,
(short)btBroadphaseProxy.CollisionFilterGroups.CharacterFilter,
(short)(btBroadphaseProxy.CollisionFilterGroups.StaticFilter | btBroadphaseProxy.CollisionFilterGroups.DefaultFilter));
world.addAction(characterController);
... (in render - moving)
if (!load)
{
if (Gdx.input.isKeyPressed(Keys.LEFT)) {
hand.transform.rotate(0, 1, 0, 5f);
ghostObject.setWorldTransform(hand.transform);
}
if (Gdx.input.isKeyPressed(Keys.RIGHT)) {
hand.transform.rotate(0, 1, 0, -5f);
ghostObject.setWorldTransform(hand.transform);
}
// Fetch which direction the character is facing now
characterDirection.set(-1,0,0).rot(hand.transform).nor();
// Set the walking direction accordingly (either forward or backward)
walkDirection.set(0,0,0);
if (Gdx.input.isKeyPressed(Keys.UP))
walkDirection.add(characterDirection);
if (Gdx.input.isKeyPressed(Keys.DOWN))
walkDirection.add(-characterDirection.x, -characterDirection.y, -characterDirection.z);
walkDirection.scl(4f * Gdx.graphics.getDeltaTime());
// And update the character controller
characterController.setWalkDirection(walkDirection);
// And fetch the new transformation of the character (this will make the model be rendered correctly)
}
world.stepSimulation(delta, 5, 1f/60f);
if (!load)
ghostObject.getWorldTransform(hand.transform);
How to fix this?
I set up the debugDrawer, so i was able to see the shapes of the bullet objects.. and my problem was that: the ghostObject(charController) was pushing my objects down.. Although my objects were static. So i set the mass of the objects to 0 and problem is fixed. But I still dont know, how it could push static objects. But i dont care. :)
EDIT: i will accept this answer in 2 hours, because now i cant.
I have an Enemy class that deals with my monster moving and attacking. Within that class, I have another class called enemyMagic, which is a blank movieclip that serves as a masterclass to different movieclips that I will make.
So in the enemyMagic class, I add a movieclip called attack1
public var attack1:Attack1 = new Attack1;
public function EnemyMagic() {
////////////CREATE THE TIMER//////////
masterEnemyAttackTimer.addEventListener(TimerEvent.TIMER, mastertimer);
////////////ATTACKS/////////
//TIER 1//
addChild(attack1);
}
And in the enemy class, I add the enemyMagic when the enemy is attacking a certain position.
for (var k:int = 0; k < Main.tileset.length; k++)
{
if (! Main.tileset[k].tileMiddle.hitTestObject(this.enemyVisionPoint))
{
if (Main.tileset[k].tileHP !== 0)
{
attackoptions.push(Main.tileset[k]);
}
if (Main.tileset[k].tileMiddle.hitTestObject(Main.player.visionPoint))
{
addChild(enemymagic);
Main.tileset[k].outline.gotoAndStop("attack");
this.enemymagic.x = (Main.tileset[k].x);
this.enemymagic.y = (Main.tileset[k].y);
trace(enemymagic.x, enemymagic.y, Main.tileset[k].x, Main.tileset[k].y);
For some reason, the enemymagic is tracing the exact same number as the tile's x and y, but it isn't adding it on the tile. It adds it way off the screen. I think it might be because it starts on the enemy's x and y and then calculates?
So my question is how can I get the enemymagic movie clip to get exactly on the position of the tile?
You can do two things. First, when you do a plain addChild() the base coordinate system of the child is the one of its parent, which is your Enemy instance, which is of course at somewhere nonzero. And then you assign it the coordinates of Main.tileset[k] which has a different parent (most likely instance of Main). This creates the distance you speak of. So, in order to locate your magic over the exact tile, either use this.globalToLocal(Main.tileset[k].localToGlobal(PZERO)) where PZERO is a new Point() constant (or write new Point() instead of PZERO, but this will create another empty Point object and will quickly escalate), or do an addChild() directly to the tile you are attacking with unaltered coordinates.
I'm making a space navigation game. So it starts with the user on the spaceship and then when he press the up key the ship goes forward, the 'map' is always different, I have 5 variations of stars and 2 variations of planets, so they basically 'spawn' randomly while the user navigates. I can make the key detection, the movie clips generator code, but I don't know how do I make the navigation code, I mean how do I make the viewport move when the user press the key, ... I've saw a code that I didn't understand too well that the guy basically created a giant movie clip that moves according to the key that was pressed. That won't work in my case because I want it to generate everything randomly and when the user press the down arrow, I want it to go back, with the same 'map' that he was before. Please help me out guys I'm totally confused with all this viewport thing. And also, I want the game to run fast, I'm kind of new to the Action Script, and I don't know if it gets heavy if you are rendering objects that are not being displayed, if so will a simple 'obj.visible = false' works? Thanks in advance.
What I do here is:
Create a Map class with a property camera which is another custom class MapCamera.
The MapCamera has five properties:
_x
_y
map - a reference to the instance of Map owning this MapCamera
offsetX
offsetY
The offset values represent the x and y spacing from the left and top edges of the screen, which should be set to half of the stage width and height so that the camera will centre on the stage correctly.
The _x and _y properties are private, and have getters and setters.
The getters are pretty basic:
public function get x():Number{ return _x; }
public function get y():Number{ return _y; }
The setters are where the viewport will be altered, like so:
public function set x(n:Number):void
{
_x = n;
map.x = -(_x + offsetX);
}
public function set y(n:Number):void
{
_y = n;
map.y = -(_y + offsetY);
}
From here, you add your children into the Map container and then can simply go:
map.camera.x = player.x;
map.camera.y = player.y;
Which will cause the player to always be in the centre of the screen.