原版的: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 be downloaded 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
= (
int)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 right and left, or up and down) 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.

Use when:

  • 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.

Avoid when:

  • 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 and transformation 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).

Code Example:

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.

-(void)
update:(ccTime)delta
{
    //
get the layer's CCCamera values
    float

centerX, centerY, centerZ;
    float

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 *
0.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,
0.0f,
_worldSize.width - _screenSize.width);
    centerY
= eyeY = clampf(centerY,
0.0f,
_worldSize.height - _screenSize.height);
     
    //
update the camera values
    [self.camera
setCenterX:centerX centerY:centerY centerZ:centerZ];
    [self.camera
setEyeX:eyeX eyeY:eyeY eyeZ:eyeZ];
}

Use when:

  • Your game needs a perspective view, or rotation animations, or elaborate zooming actions that would be difficult to implement with any of the other scrolling methods.

Avoid when:

  • 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.

Code Example:

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(
0,
0,
_worldSize.width, _worldSize.height);
[self
runAction:[CCFollow actionWithTarget:_player worldBoundary:worldBoundary]];

Use when:

  • You need to get started quickly with a scrolling view.

Avoid when:

  • 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.

Code Example:

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.

-(void)
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
    if

(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;
         
    float

distanceFromCurrentToTargetPos = ccpLength(ccpSub(targetPos, currentPos));
    if

(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;
}

Use when:

  • You need full flexibility over the scrolling behavior.

Avoid when:

  • 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 you can use the
CCFollow code as basis. Here's another good starting point but 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.


版权声明:本文博客原创文章。博客,未经同意,不得转载。

PS: 转载请注明出处 http://blog.csdn.net/ouyangtianhan

Cocos2d:使用 CCCamera 做滚动效果 (Four Ways of Scrolling with Cocos2D)的更多相关文章

  1. jq PC做滚动效果经常用到的各类参数【可视区判断】

    获取 浏览器显示区域 (可视区域)的高度 :  $(window).height();  获取浏览器显示区域(可视区域)的宽度 :  $(window).width();  获取页面的文档高度: $( ...

  2. iframe框架里镶嵌页面;<marquee>:滚动效果;<mark>做标记;内联、内嵌、外联;选择器

    标签:①②③④⑤⑥⑦★ 框架: 一.frameset:(框架集) 1.如果使用框架集,当前页面不能有body 2.cols="300,*":左右拆分,左边宽300,右边宽剩余 3. ...

  3. 使用javascript开发的视差滚动效果的云彩 极客标签 - 做最棒的极客知识分享平台

    www.gbtags.com 使用javascript开发的视差滚动效果的云彩 阅读全文:使用javascript开发的视差滚动效果的云彩 极客标签 - 做最棒的极客知识分享平台

  4. 全屏滚动效果H5FullscreenPage.js

    前提: 介于现在很多活动都使用了 类似全屏滚动效果 尤其在微信里面 我自己开发了一个快速构建 此类项目的控件 与市面上大部分控件不同的是此控件还支持元素的动画效果 并提供多种元素效果 基于zepto. ...

  5. Expression Blend4经验分享:文字公告无缝循环滚动效果

    这次分享一个类似新闻公告板的无缝循环滚动效果,相信很多项目都会应用到这个效果.之前我也百度了一下,网上的一些Silverlight的文字或图片滚动效果,都是一次性滚动的,如果要做到无缝循环滚动,多数要 ...

  6. jQuery实现滚动效果详解1

    声明:第一次写原创,本人初学,很多地方一知半解,本篇算是一个学习的笔记,欢迎批评指正,转载请注明. 今天要做的效果是在网上经常能看到多幅图片向左无缝滚动,鼠标滑过动画暂停,鼠标滑出动画继续的效果.网上 ...

  7. iOS 字体滚动效果 ScrollLabel

    写了一个简单的字体滚动效果. 用了一种取巧的方式,传入两个一摸一样的Label(当然也可以是别的视图), 话不多说,代码里面讲解. SEScrollLabel.h #import <UIKit/ ...

  8. Unity3D问题之EnhanceScollView选择角色3D循环滚动效果实现

    需求 呈现3D效果(2D素材)选择角色效果 滚动保证层级.缩放比例.间距正常尾随 循环滚动 这个界面需求一般也会有游戏会採用(貌似有挺多) 怎样实现 实现技术关键点 (3D循环效果,依据数学函数和细致 ...

  9. 整屏滚动效果 jquery.fullPage.js插件+CSS3实现

    最近很流行整屏滚动的效果,无论是在PC端还是移动端,本人也借机学习了一下,主要通过jquery.funnPage.js插件+CSS3实现效果. 本人做的效果: PC端:http://demo.qpdi ...

随机推荐

  1. Linux渗透+SSH内网转发

    http://www.jb51.net/hack/58514.html http://blog.chinaunix.net/uid-756931-id-353243.html http://blog. ...

  2. Leetcode: Spiral Matrix. Java

    Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral or ...

  3. SecureCRT 6.7.1 注冊机 和谐 破解 补丁 方法

    之前一直在用SecureCRT 6.5.3 版本号,和谐补丁也好找,甚至中文版本号也可找到(眼下仅仅找到了SecureCRT.6.2.0) 可是换为 6.7.1 后就怎么也注冊不了了.. 没办法试了各 ...

  4. oracle迁移mysql数据库注意(转)

    oracle转mysql修改: 1. substr() substr( string , 0, 10) 这里测试 必须从 第一位获取 既是 substr(string , 1 , 10)2. to_c ...

  5. 日历的问题C语言,C++(boost),python,Javascript,Java和Matlab实现

    今天看到一个很有意思的话题,例的标题叙述性描述,下面: 根据以下信息来计算1901年1月1至2000年12月31适逢星期日每个月的第一天的合伙人数量? a)  1900.1.1星期一 b)  1月,3 ...

  6. hdu 2191 悼念512四川汶川大地震遇难者——如今宝,感恩生活

    悼念512四川汶川大地震遇难者--如今宝,感恩生活 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Jav ...

  7. Ubuntu下hadoop2.4搭建集群(单机模式)

    一  .新建用户和用户组 注明:(这个步骤事实上能够不用的.只是单独使用一个不同的用户好一些) 1.新建用户组 sudo addgroup hadoop 2.新建用户 sudo adduser -in ...

  8. 004串重量 (keep it up)

    设计算法并写出代码移除字符串中反复的字符,不能使用额外的缓存空间. 注意: 能够使用额外的一个或两个变量,但不同意额外再开一个数组拷贝. 简单题直接上代码: #include <stdio.h& ...

  9. 【原创】leetCodeOj --- Find Minimum in Rotated Sorted Array II 解题报告

    题目地址: https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/ 题目内容: Suppose a sort ...

  10. 微渠道发展 BAE交通运输平台和java呼声,微信mysql数据库开发实例 --图文开发教程

    持续更新 BAE java开展mysql数据库 图文教程 BAE java语言发展mysql源码下载: 目前微信的发展.BAE开展.java开展.mysql教程开发非常,的介绍基于BAE平台.java ...