Camera for a 2D project

Camera for a 2D project
0.0 0

#22

Is there any sample code to show how to use an orthographic camera in a 2D game, ie: so i can use the camera to move/zoom/rotate around the game world?

I can create a camera on a scene, but it doesn’t seem to affect any Sprite nodes (I’ve set the sprite camera mask) so I don’t think I’m doing it right.


#23

UPDATE: I have cameras sort of working, including rendering sprites to a low-res rendertexture for later display.

Camera panning works as expected with just camera->setPosition(…)

However, the rotation and zoom origin seem to be the camera position, and this is stuck to the bottom left (setAnchorPoint has no effect). Also the scale factor is inverted (larger scale = smaller sprites).

camera->setRotation(15);

camera->setScale(2.0f);

I had better results applying rotation and scale via camera->setAdditionalProjection(…) (after modifying Camera.cpp as below) but transforms applied this way are additive each frame.

void Camera::setAdditionalProjection(const Mat4& mat)
{
    _projection = mat * _projection;
    _viewProjectionDirty = true;  // change
    _frustumDirty        = true;  // change
    getViewProjectionMatrix();
}

Surely someone has made a working 2D camera for a Cocos game?


#24

While essentially the same thing, just inverted, I’ve found having my own “camera” that manages the game board/level position/rotation/scale to be more intuitive or easier to use. Partly because using the Camera class just never produced what I wanted, but I’m positive one could get it working as you seem to be close to this.

I do use the camera class to separate HUD (default camera) from the game (camera USER 1, depth < HUD) mostly just because I usually use the depth buffer and its easier to just draw HUD in a fully separate pass w/Camera, instead of having to rely on local/global Z ordering.

2c


#25

Can you describe how you’ve implemented that @stevetranby ?

You’re right, I think you could get the built in Camera to “work” by modifying getViewProjectionMatrix, or applying delta rotation and scale each frame via setAdditionalProjection, but maybe your approach is cleaner?


#26

Camera would be cleaner technically, since it’d be one “dirty” transform and wouldn’t need to update any vertices.

That said, yes I believe the way I’m doing it is the simpler one to understand from a code/math point of view (but it’s still possible this observation is wrong).

Basically:

SCENE 
-> Background Layer Node -> Children
-> Gameplay Layer Node -> Player / Enemis / World that Player interacts w/etc
-> HUD and other layers that don't move when the virtual camera moves.

// Background - can have parallax and other behaviors, but is presumed to be 
// mostly unaffected by camera. the one feature you could "pass-through" would
// be camera pan/zoom/rotate could also apply to this separately such that you
// don't need to have background as a child of the root camera gameplay node. 

// If you want to have non-camera nodes (even transiently such that they 
// can toggle following camera and not)
GamePlayLayerNode
-> Virtual Camera Transform Node (or can just use the GamePlay root node itself 
     -> Camera affected background node(s)
     -> World Nodes (tilemap, blocks, physics wall sprites, etc)
     -> All nodes/entities that track with camera

// Pan Camera Right
auto p = cameraNode->getPosition();
p.x -= cameraSpeedOrPositionDeltaPerFrame;
cameraNode->setPosition(p);

// Rotate Camera Right
auto rot = cameraNode->getRotation();
rot.x -= cameraRotationClockwise; // actually rotates opposite
cameraNode->setRotation(rot);

// Zoom In
scale *= cameraZoomDeltaSpeedRatePerFrame; // scale increases when zoom in

// Zoom Out (may subtract instead, also protect against divide by zero)
scale /= cameraZoomDeltaSpeedRatePerFrame; // scale decreases when zoom in

// etc, may want to look into enhanced camera capabilities like 
// follow windows and target points

#27

Edited above. It may look overly complicated.

The main point is this: when transforming a root Node as the virtual camera everything is inverted with respect to the camera itself.

You’ll have to decide how you want to organize, whether to use multiple root nodes where camera transformations are applied or whether to just nest them as children and transform a single node instead.


#28

Lastly, there’s also the various enhancements and concepts around “advanced” camera capabilities that I hinted at in my replies.

Just a few things you can add to your camera management API:

  • Follow multiple-targets where camera can zoom out as targets move farther apart.
  • Only follow when target moves outside of a region
  • Switch targets
  • Lock to a target or other
  • Screen Shake

p.s. finally as a walk-back everything I just said, if the Camera API became advanced with all of these capabilities, then maybe that would be the easy recommendation in the future.


#29

Here’s a great post about the full enhancements one might make.


#30

Had a go at your scheme but I have a few questions.

What do you have the position and anchor point of the camera node set to?
eg: if I wanted to have a sprite at position 0,0 shown in the bottom left of the screen, what should I set the camera position to?

Is your camera rotation around the center of the screen? Mine was still around the camera position.


#31

Now that you mention that I checked over my code.
I oversimplified it apparently, and also I haven’t actually used rotation in mine.
Sorry about offering the advice that ultimately is wrong.

There’s probably a 3rd superior version of this that I’m unaware of :smiley:

Now that you remind me of this extra issue(s) I might recommend going back to using the Camera and transforming its matrices???


Anyway, I would probably work on the math for updating the anchor point + position such that you can rotate it around the anchor point at the center of the screen.

In our game(s) I’ve only done panning and zooming. In that case I have to map from screen center (or pinch center) to world coordinates and when zooming I did need to add an offset based on the new zoom scale position delta that occurs when scaling.

I just use anchor 0,0 and have world coord 0,0 == root node’s position.

From my experience it really just all comes down to math, and for me drawing things out.

Also, for zoom/rotate you’ll probably have to do an inverse position (to move to some “center”), do the scale/rotate, and then re-position back. The net effect of this would be similar to using the anchorpoint instead. The key again will be to not have the game world jump around due to zoom/rotate.

I can probably post my PanZoomLayer and my CameraManager if you want to check it out.


#32

Always interested in code :slight_smile:

I replaced getViewMatrix in Camera with the calculation from a camera class I made for another project as below. This isn’t possible without modifying the core Camera class as getViewMatrix isn’t virtual.

Not optimised :slight_smile:

const Mat4& getViewMatrix() const {
    Mat4 t1;
    Mat4::createTranslation(-getPositionX(), -getPositionY(), 0, &t1);

    Vec3 origin(Game::DW2, Game::DH2, 0);
    Mat4 o1;
    Mat4::createTranslation(origin, &o1);

    Mat4 s1;
    Mat4::createScale(getScale(), getScale(), 1, &s1);

    Mat4 r1;
    Mat4::createRotationZ(DEG2RAD(getRotation()), &r1);

    Mat4 o2;
    Mat4::createTranslation(-origin, &o2);

    _view = t1 * o1 * s1 * r1 * o2;
    _viewInv = _view.getInversed();

    _viewProjectionDirty = true;
    _frustumDirty = true;

    return _view;
}

“origin” is the rotation and zoom origin, in this case hardcoded to be the centre of the view. The Camera node position, scale and rotation should be set in absolute terms, ie: no longer relative to any parent.

It looks like this now:
2018-01-08 00_20_48

I’ve posted the source for this test scene here.

This is the basics. It can be tidied up and advanced features as you describe added.

I haven’t tested the unproject methods, but they work off the matricies so I’m not expecting any issues.


#33

Here’s some code, it probably won’t be of any help.
It’s a bit hacky, and probably a terrible way to handle this :smiley:
This thread caught my eye because I’m also interested in potentially moving to using the Camera class instead.


STLayerPanZoom (modified version of CCLayerPanZoom)
Low-Level “camera” manipulation and input handling.

https://gist.github.com/stevetranby/6813fbb427fb57a0cc2ed268b8d15ac0 .

I believe this came from this discussion, but otherwise from another repo/source. modified from an original source repo, possibly ObjC that I ported)
CCLayerPanZoom for cocos2dx .


CameraManager (managers are dumb, but it is what it is :D)
Mainly enhances w/somewhat “higher level” API

https://gist.github.com/stevetranby/32bf3f27b389e5497750b33edeb8c8cc .


Use Case
https://gist.github.com/stevetranby/3d69781cdc3306493a880d936928190e .


#34

Not sure I need a controllable pan/zoom layer for this, but thanks. Should be easy enough to integrate with my modified camera if I do though.

Now, where do we go to vote for v4 features :smiley: ?