Cocos2d:使用 CCCamera 做滚动效果 Four Ways of Scrolling with Cocos2D

原文:http://www.koboldtouch.com/display/IDCAR/Four+Ways+of+Scrolling+with+Cocos2D


There are two classes of scrolling,"fake" and "real". Altogether there are four ways to create a scrolling view in Cocos2D: with CCCamera,with CCFollow,manually moving a layer and "faking it". I'll discuss each approach and show their advantages and disadvantages as well as point out for which types of games they work best.

The example projects for this article can bedownloaded from github.

Fake Scrolling - Merely creating the Illusion of Scrolling

Ideal for "endless scrolling" games. Typical genres include jumping or running games like Doodle Jump and Canabalt,as well as shoot'em ups.

These games mainly scroll along one axis,often scrolling only in one direction. The other axis has a limited range or is not scrolled at all. The fake scrolling approach prevents the coordinates of game objects to "explode" to similarly infinite coordinate points. This could introduce rounding errors in floating point values which may accumulate over time,causing inaccuracies at later stages of the game.

To create the illusion of scrolling,at least two background image sprites are needed. Four are needed if you also want to scroll a little along the minor scrolling axis. The background images must be repeating seamlessly,and each must be at least the size of the screen. The trick is to use the two or four images and move them in the opposite direction of where the player is supposedly heading. This creates the illusion that the player is actually moving in a certain direction. Once one background image has scrolled entirely outside the view,it will be repositioned by adding the width or height of the screen(depending on major scrolling axis) to the position of all background sprites. The player does not notice this repositioning,which allows the background sprites to continue moving in the current direction seemingly endlessly.

Game objects (enemies,items,etc.) usually enter the screen from the side towards which the player is moving,but any location is possible. You would normally spawn these objects one screen ahead (or behind) what is currently visible,then move them on screen. Movement of enemies can be fixed if the scrolling occurs at a constant rate and never stops. Otherwise all game object's positions must be updated with the delta scroll value every time to keep them fixed at their current position. You would do this at a central location before or after moving the game objects. Without this,varying the scrolling speed would also speed up or slow down the movement of game objects,which is probably not what you want. This voids the use of CCMove* actions because they don't consider external modification of the position property while they're running.

From a game construction perspective this type of scrolling lends itself well for randomly generated worlds. You can alway use a certain location in front of or behind the scrolling direction to spawn new game objects. You always have only a relatively short list of game objects that are alive in this "world",so each object can be allowed to run relatively complex code. And you can dismiss game objects easily by checking if they have moved past the threshold location. All coordinates are relative to screen coordinates,so at most you're dealing with a value range 3 times that the size or width of the screen (one screen ahead,one screen behind,and the actual screen - more if you allow scrolling along both axes).

Using object pooling you can reuse a predetermined amount of game objects of a certain type and thus also avoid spawning too many of the same type. For example if there can be 10 Zombies on the screen at once,you'd create 10 Zombies up front,set them to inactive and simply reposition and activate them to spawn one. When you have 10 Zombies on the screen and game logic dictates to spawn another one,you can't and no new Zombie gets spawned. This design lends itself well to high performance and has built-in limits that avoid many (but not all) possibly unfair game situations that can occur in randomly generated worlds.

Code Example:

The scrolling effect is created by moving the background images. This example uses two images moving from right to left,to give the impression of movement from left to right. You can also put the background images into a separate layer and move them all at once by adjusting the layer's position.

-( void ) update:(ccTime)delta
{
CGPoint bg1Pos = _bg1.position;
CGPoint bg2Pos = _bg2.position;
bg1Pos.x -= kScrollSpeed;
bg2Pos.x -= kScrollSpeed;
// move scrolling background back from left to right end to achieve "endless" scrolling
if (bg1Pos.x < -(_bg1.contentSize.width))
{
bg1Pos.x += _bg1.contentSize.width;
bg2Pos.x += _bg2.contentSize.width;
}
// remove any inaccuracies by assigning only int values (this prevents floating point rounding errors accumulating over time)
bg1Pos.x = ( int )bg1Pos.x;
bg2Pos.x = ( )bg2Pos.x;
_bg1.position = bg1Pos;
_bg2.position = bg2Pos;
}

Use when:

  • Your game should scroll endlessly or very far.
  • Your game primarily scrolls along one axis,perhaps even in one direction. A "tube-like" world.
  • Your game randomly generates most or all of the world as the game progresses.

Avoid when:

  • Your game needs to scroll in any direction.
  • Your game uses a "designed" world (predetermined locations of objects,backgrounds,etc) of finite size.
  • Your game's background images do not repeat.

All objects use relative screen coordinates,easy to work with. No "explosion" of coordinate values in infinitely scrolling worlds.
No conversion of touch coordinates necessary.
Ideal for randomly generating content.
The player character stays fixed or at least only moves within screen coordinates or a a little more.

Code complexity increases if direction of scrolling can go both ways (ie rightandleft,or upanddown) or allows scrolling at variable speed. CCMove* actions can not be used in these cases.
Not suitable for two equally valid scrolling directions (ie square worlds). Main scrolling direction is either vertical or horizontal,with the other axis either not scrolling at all or only within a small range.
Adapting to various screen aspect ratios (iPhone,iPhone widescreen,iPad) requires extra care.

"Real" Scrolling - Using the Screen as View onto the World

Ideal for larger worlds. The player can travel freely in all four directions at any speed. Typical examples include games where the player traverses large worlds,for example Zelda or Super Mario.

You can scroll a world either by using CCCamera or my adjusting the position of a parent node. The CCFollow method falls into the latter category. The main difference between CCCamera and CCFollow or layer movement is that CCCamera moves the viewport over the fixed (non-moving) world whereas CCFollow or layer movement move the world underneath the fixed camera. The end result is the same.

  • Your game should scroll equally in any direction.
  • Your game is a designed world,or randomly generated but has a finite size.
  • Your game world is a tilemap.
  • Your game needs to scroll endlessly or very far (far = over ten thousands points in any direction).
  • Your game relies on randomly creating its world. Even if it needs to scroll in any direction fake scrolling may be a better solution.
  • Scrolling with CCCamera

    CCCamera is a wrapper for the OpenGL gluLookAt function. It has the most flexibility allowing free rotating,zoom and even perspective views. But it's also more difficult to work with and not fully compatible with all aspects of cocos2d. CCMenu won't work,and converting touch coordinates will be a challenge. The biggest problem of CCCamera is probably that there's a serious lack of expertise - there are many CCCamera related questions on the cocos2d forum and in other places,but hardly any good answers. Most questions simply remain unanswered.CCCamera works differently than anything else in cocos2d,and affects cocos2d rendering in ways you may not understand or foresee.Therefore I strongly discourage anyone from using CCCamera unless that anyone is familiar with OpenGL viewport basics,gluLookAt andtransformation matrices (conversion of screen to view coordinates).

    Even if you do know about these things,use CCCamera only when you need the full flexibility of a zooming,rotating or perspective camera view. For example if you want a driving game which should keep the car always facing up,rotating the world relative to where the car is going,then you'd have to use the CCCamera. Implementing this with layer movement will be more difficult and certainly requires a different approach to how the car movement is handled (the car would actually never rotate by itself,and would always face in a fixed direction).

    You first need to get all the camera values by reference (&centerX) in C programming language fashion. Then you need to update both center and eye to be at the same position in order to maintain an orthogonal (top down) view. Introducing 3D effects is as simple as allowing center and eye to deviate. Then you must set all values back to the camera,including those you haven't modified to ensure you don't accidentally change one of the values you didn't modify.

    For example,if you hard code the value 0.0 when setting centerZ and eyeZ,for some reason the screen goes black even though the values for centerZ and eyeZ returned from CCCamera are 0.0 as well. This is just one of the oddities of CCCamera.

    // get the layer's CCCamera values
    float centerX,centerY,centerZ;
    eyeX,eyeY,eyeZ;
    [self.camera centerX:&centerX centerY:&centerY centerZ:&centerZ];
    [self.camera eyeX:&eyeX eyeY:&eyeY eyeZ:&eyeZ];
    // set camera center & eye to follow player position (centered on screen)
    centerX = eyeX = (_player.position.x - _screenSize.width * 0 .5f);
    centerY = eyeY = (_player.position.y - _screenSize.height * .5f);
    // Make sure not to scroll near the world boundaries,to prevent showing the area outside the world.
    // Note: this code does not take into account zooming out or in by modifying the camera's eyeZ value.
    centerX = eyeX = clampf(centerX, .0f,_worldSize.width - _screenSize.width);
    centerY = eyeY = clampf(centerY,_worldSize.height - _screenSize.height);
    // update the camera values
    [self.camera setCenterX:centerX centerY:centerY centerZ:centerZ];
    [self.camera setEyeX:eyeX eyeY:eyeY eyeZ:eyeZ];
    }

  • Your game needs a perspective view,or rotation animations,orelaborate zooming actions that would be difficult to implement with any of the other scrolling methods.
  • Always,by default.
  • If you do not have an understanding (and don't want to learn about) gluLookAt,the OpenGL viewport,coordinate conversion.
  • If you want to use CCMenu in your in-game user interface.
  • Scroll in any direction.
    CCCamera uses world coordinates.
    Also allows to zoom in/out of the world as well as rotation and even perspective (3D) effects.

    CCCamera is not well documented or tutorialized. It's also rarely used so it's hard to get (good) answers to CCCamera related questions. Many issues require a deeper understanding of OpenGL.
    CCCamera uses an awkward C-style interface for getting and setting position and look at points.
    Touch coordinates must be converted to world coordinates.Conversion is complex,involving camera coordinates and "magic numbers".
    Not fully compatible with all nodes. For example CCMenu will not work correctly because it does not implement the necessary touch conversion.
    When zooming with CCCamera there is a value range threshold where objects start to disappear. This threshold is different on different devices.

    Scrolling with CCFollow

    CCFollow works by changing the position of the node that is running the CCFollow action relative to the position of the followed node. Typically you'll run it on the layer that contains the node (usually the player-controlled object) to be followed.

    CCFollow should only run on non-drawing nodes (CCNode,CCLayer,CCScene) and should follow a node that is one of its direct children. Especially if the followed node is not a direct children you might notice that the screen follows the object but it does so at an offset.

    CCFollow is the easiest way to create a scrolling view,but it's also the least flexible. You can choose between limitless scrolling,or scrolling that stops at predefined (world) boundaries. That is all. For everything else you'll have to subclass CCFollow and add whatever code you need to improve the scrolling behavior.

    CCFollow requires the least amount of code to implement scrolling. Just add this code in init or onEnter to allow the layer (self) to scroll so that the followed node (_player) is always centered on screen. Additionally this code enables the world boundaries check so that scrolling stops before any part outside of the world becomes visible. The player is still able to move up to the world border,or even beyond that if you don't limit the player's movement as well.

    // Let the layer follow the player sprite,while restricting the scrolling to within the world size.
    // This is really all you need to do. But it's really all you *can* do either without modifying CCFollow's code.
    CGRect worldBoundary = CGRectMake( ,_worldSize.width,_worldSize.height);
    [self runAction:[CCFollow actionWithTarget:_player worldBoundary:worldBoundary]];

  • You need to get started quickly with a scrolling view.
  • You need to influence any scrolling parameters,such as how closely the node is followed or how the scrolling accelerates and decelerates.
  • Scroll in any direction.
    Always keeps the followed node centered.
    Allows to specify world boundaries to clamp scrolling at world borders.

    No control whatsoever. Follow speed,follow range,acceleration/deceleration of scrolling movement,prevent scrolling in a particular direction,and so on - none of that is supported.
    Changes position of the node running the action. Typically that will be a layer,so it shouldn't matter.
    Touch coordinates must be converted to world coordinates using built-in convertToWorldSpace.
    Implementing zoom in/out centered on a particular object is difficult. Rotating relative to an object is very difficult.

    Custom Scrolling by subclassing CCFollow

    This is essentially the same method that CCFollow uses,but you will have to implement any additional behavior yourself. Typically you will want to subclass CCFollow because that'll be easier,but you can also rip out the guts of CCFollow and put the scrolling code in your layer's update method. For example if you want the scrolling to happen only when the followed node is getting close enough to one of the screen borders,you'd have to subclass CCFollow,override the step method,put the original code back in and then start modifying how CCFollow updates the position. An example is given in the CCCustomFollow demo.

    The main downside caused by moving an underlying layer is that the position of that layer is the inverse of the actual movement.For example to scroll 100 pixels to the right,you have to subtract 100 pixels from the layer's position,which then may have a negative position of -100. You might find it counterintuitive that the targetPos CGPoint in CCFollow uses mostly negative coordinates - because to scroll to the right and up,the target node (usually the layer) must move in the opposite direction,to the left and down.

    First create a subclass of CCFollow,in this case I named it CCCustomFollow. Initialize it like CCFollow above,and override the step method in CCCustomFollow. You can start with the CCFollow code as basis,understand it first,then start modifying it.

    This example prevents scrolling while the followed node is within 120 points of the center. Once the followed node crosses that distance,the scrolling will start following the followed node. This is a very simplified example of "border scrolling". You still need to improve this in several ways,for example to ensure that the scrolling correctly stops at world boundaries and perhaps to improve it so that the non-scrolling area is not a circle but a rectangle. You might also attempt to scroll faster when the followed node leaves the threshold so that the followed node becomes centered on the screen again. All of those tasks aren't exactly trivial,but they're manageable if you have a reasonably solid understanding of trigonometry.

    ) step:(ccTime) dt
    // The targetPos coordinate values become more negative the more the followed node moved to the right and up.
    // This is because the layer is moved in the opposite direction of where the followed node is heading.
    CGPoint targetPos = ccpSub(halfScreenSize,followedNode_.position);
    targetPos.x = clampf(targetPos.x,leftBoundary,rightBoundary);
    targetPos.y = clampf(targetPos.y,bottomBoundary,topBoundary);
    // initialize currentPos once
    (isCurrentPosValid == NO)
    {
    isCurrentPosValid = YES;
    currentPos = targetPos;
    }
    // if current & target pos are this many points away,scrolling will start following the followed node
    const float kMaxDistanceFromCenter = 120 .0f;
    distanceFromCurrentToTargetPos = ccpLength(ccpSub(targetPos,currentPos));
    (distanceFromCurrentToTargetPos > kMaxDistanceFromCenter)
    {
    // get the delta movement to the last target position
    CGPoint deltaPos = ccpSub(targetPos,previousTargetPos);
    // add the delta to currentPos to track followed node at the distance threshold
    currentPos = ccpAdd(currentPos,deltaPos);
    [target_ setPosition:currentPos];
    }
    previousTargetPos = targetPos;
    }

  • You need full flexibility over the scrolling behavior.
  • You need functionality only CCCamera can provide,such as rotation relative to followed node's movement direction.
  • Scroll in any direction.
    More scrolling flexibility - if implemented.

    Do it yourself,but youcan use the CCFollow code as basis.Here's anothergood starting pointbut the code has "All Rights Reserved".
    May feel counter-intuitive,since position of the node (layer) that causes the scrolling effect is the inverse of the actual scrolling direction.
    Touch coordinates must be converted to world coordinates using built-in convertToWorldSpace.
    Implementing zoom in/out centered on a particular object is difficult. Rotating relative to an object is very difficult.

    版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

    相关推荐


        本文实践自 RayWenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建实时纹理、如何用Gimp创建无缝拼接纹
    Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@163.com微信公众号:HopToad 欢迎转载,转载标注出处:http://blog.csdn.netotbaron/article/details/424343991.  软件准备 下载地址:http://cn.cocos2d-x.org/download 2.  简介2.1         引用C
    第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从Cocos2D-x官网上下载,进入网页http://www.cocos2d-x.org/download,点击Cocos2d-x以下的Download  v3.0,保存到自定义的文件夹2:从python官网上下载。进入网页https://www.python.org/downloads/,我当前下载的是3.4.0(当前最新
        Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发引擎,易学易用,支持多种智能移动平台。官网地址:http://cocos2d-x.org/当前版本:2.0    有很多的学习资料,在这里我只做为自己的笔记记录下来,错误之处还请指出。在VisualStudio2008平台的编译:1.下载当前稳
    1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《最强大脑》娱乐节目。将2048改造成一款挑战玩家对数字记忆的小游戏。邮箱:appdevzw@163.com微信公众号:HopToadAPK下载地址:http://download.csdn.net/detailotbaron/8446223源码下载地址:http://download.csdn.net/
       Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试以QtCreatorIDE来进行CMake构建。Cocos2d-x3.X地址:https://github.com/cocos2d/cocos2d-x1.打开QtCreator,菜单栏→"打开文件或项目...",打开cocos2d-x目录下的CMakeLists.txt文件;2.弹出CMake向导,如下图所示:设置
     下载地址:链接:https://pan.baidu.com/s/1IkQsMU6NoERAAQLcCUMcXQ提取码:p1pb下载完成后,解压进入build目录使用vs2013打开工程设置平台工具集,打开设置界面设置: 点击开始编译等待编译结束编译成功在build文件下会出现一个新文件夹Debug.win32,里面就是编译
    分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net前言上次用象棋演示了cocos2dx的基本用法,但是对cocos2dx并没有作深入的讨论,这次以超级马里奥的源代码为线索,我们一起来学习超级马里奥的实
    1. 圆形音量button事实上作者的本意应该是叫做“电位计button”。可是我觉得它和我们的圆形音量button非常像,所以就这么叫它吧~先看效果:好了,不多解释,本篇到此为止。(旁白: 噗。就这样结束了?)啊才怪~我们来看看代码:[cpp] viewplaincopyprint?CCContro
    原文链接:http://www.cnblogs.com/physwf/archive/2013/04/26/3043912.html为了进一步深入学习贯彻Cocos2d,我们将自己写一个场景类,但我们不会走的太远,凡是都要循序渐进,哪怕只前进一点点,那也至少是前进了,总比贪多嚼不烂一头雾水的好。在上一节中我们建
    2019独角兽企业重金招聘Python工程师标准>>>cocos2d2.0之后加入了一种九宫格的实现,主要作用是用来拉伸图片,这样的好处在于保留图片四个角不变形的同时,对图片中间部分进行拉伸,来满足一些控件的自适应(PS: 比如包括按钮,对话框,最直观的形象就是ios里的短信气泡了),这就要求图
    原文链接:http://www.cnblogs.com/linji/p/3599478.html1.环境和工具准备Win7VS2010/2012,至于2008v2版本之后似乎就不支持了。 2.安装pythonv.2.0版本之前是用vs模板创建工程的,到vs2.2之后就改用python创建了。到python官网下载版本2.7.5的,然后
    环境:ubuntu14.04adt-bundle-linux-x86_64android-ndk-r9d-linux-x86_64cocos2d-x-3.0正式版apache-ant1.9.3python2.7(ubuntu自带)加入环境变量exportANDROID_SDK_ROOT=/home/yangming/adt-bundle-linux/sdkexportPATH=${PATH}:/$ANDROID_SDK_ROOTools/export
    1开发背景游戏程序设计涉及了学科中的各个方面,鉴于目的在于学习与进步,本游戏《FlappyBird》采用了两个不同的开发方式来开发本款游戏,一类直接采用win32底层API来实现,另一类采用当前火热的cocos2d-x游戏引擎来开发本游戏。2需求分析2.1数据分析本项目要开发的是一款游
    原文链接:http://www.cnblogs.com/linji/p/3599912.html//纯色色块控件(锚点默认左下角)CCLayerColor*ccc=CCLayerColor::create(ccc4(255,0,0,128),200,100);//渐变色块控件CCLayerGradient*ccc=CCLayerGradient::create(ccc4(255,0,0,
    原文链接:http://www.cnblogs.com/linji/p/3599488.html//载入一张图片CCSprite*leftDoor=CCSprite::create("loading/door.png");leftDoor->setAnchorPoint(ccp(1,0.5));//设置锚点为右边中心点leftDoor->setPosition(ccp(240,160));/
    为了答谢广大学员对智捷课堂以及关老师的支持,现购买51CTO学院关老师的Cocos2d-x课程之一可以送智捷课堂编写图书一本(专题可以送3本)。一、Cocos2d-x课程列表:1、Cocos2d-x入门与提高视频教程__Part22、Cocos2d-x数据持久化与网络通信__Part33、Cocos2d-x架构设计与性能优化内存优
    Spawn让多个action同时执行。Spawn有多种不同的create方法,最终都调用了createWithTwoActions(FiniteTimeAction*action1,FiniteTimeAction*action2)方法。createWithTwoActions调用initWithTwoActions方法:对两个action变量初始化:_one=action1;_two=action2;如果两个a
    需要环境:php,luajit.昨天在cygwin上安装php和luajit环境,这真特么是一个坑。建议不要用虚拟环境安装打包环境,否则可能会出现各种莫名问题。折腾了一下午,最终将环境转向linux。其中,luajit的安装脚本已经在quick-cocos2d-x-develop/bin/中,直接luajit_install.sh即可。我的lin
    v3.0相对v2.2来说,最引人注意的。应该是对触摸层级的优化。和lambda回调函数的引入(嗯嗯,不枉我改了那么多类名。话说,每次cocos2dx大更新。总要改掉一堆类名函数名)。这些特性应该有不少人研究了,所以今天说点跟图片有关的东西。v3.0在载入图片方面也有了非常大改变,仅仅只是