内层滚动条随外层滚动条滚动_滚动条的剖析

内层滚动条随外层滚动条滚动

游戏开发 (Game Development)

How 2D platform games achieve infinite scrolling

2D平台游戏如何实现无限滚动

Many of the biggest game hits on the App Store & Play Store fall into the “Endless Runner” category. The simplicity of their 2-dimensionality and single-touch control make them accessible to a wide range of users. They also happen to be some of my personal favorites. This article examines how they can be built on iOS to achieve their “infinite scrolling” effect.

M之外的任何在App Store和Play Store秋天最大的游戏点击进入“无尽的亚军”类。 二维的简单性和单点触摸控制使它们可供广泛的用户使用。 他们碰巧也是我个人的最爱。 本文探讨了如何在iOS上构建它们以实现其“无限滚动”效果。

目标: (Goals:)

  • From Scratch: Game engines and 3rd-party libraries will only obscure the fundamentals and add bloat. Building this demo using pure Swift will shine a light on the basic mechanics required.

    从头开始 :游戏引擎和第三方库只会模糊基础知识并增加膨胀。 使用纯Swift构建此演示将阐明所需的基本原理。

  • Endless scrolling: No level maps. The action should never end, with no performance penalties or memory leaks.

    无休止的滚动 :无关卡。 该操作永远不会结束,不会造成性能损失或内存泄漏。

  • Themes: Showcase several visual themes to help illustrate common elements and clarify the structure of the demo.

    主题:展示几个视觉主题,以帮助说明常见元素并阐明演示的结构。

  • Dynamic Drawing: As a bonus challenge, all game graphics should be procedurally drawn (no pre-rendered artwork). More on this and how it affects performance later.

    动态绘图 :作为奖励挑战,应按程序绘制所有游戏图形(无预渲染图稿)。 有关此内容以及它以后如何影响性能的更多信息。

(Code)

The complete code for this demo can be found here

此演示的完整代码可在此处找到

为什么选择SpriteKit? (Why SpriteKit?)

For iOS developers, SpriteKit offers a low barrier-to-entry as there are no complex frameworks or engine libraries to learn. SpriteKit has a flat learning curve; if you know Swift (or Objective-C), you can pick it up with reasonable effort. Using SpriteKit also precludes the need for integrating 3rd-party dependencies and keeping them up-to-date. And since this article examines 2D games, it makes sense to use a native 2D framework which is advertised to provide fast and low-cost performance.

对于iOS开发人员而言,SpriteKit的入门门槛低,因为它不需要学习复杂的框架或引擎库。 SpriteKit的学习曲线平坦; 如果您了解Swift(或Objective-C),则可以尽力而为。 使用SpriteKit还排除了集成第三方依赖项并使它们保持最新的必要性。 并且由于本文研究了2D游戏,因此使用本机2D框架是有意义的,该框架被宣传为提供快速和低成本的性能。

视觉层次 (Visual Hierarchy)

To construct a convincing game-like environment, several graphic layers need to be composited. Technically, a parallax effect can be achieved using just 2 graphic layers. This demo uses 3 more for a total of 5. It does this to create a richer visual experience. By moving some layers at varying speeds while keeping others stationary, a compelling parallax effect is produced. The following figure demonstrates a stationary atmospheric layer and 4 scrolling layers representing the foreground, mid-ground, background and distant background. In addition, a vehicle which interacts with a physical terrain further enhances the realism of the scene. Where appropriate, particle effects like snow, dust or engine exhaust can provide added ambience.

要构建令人信服的类似游戏的环境,需要合成几个图形层。 从技术上讲,仅使用2个图形层就可以实现视差效果 。 此演示总共使用了3个,总共使用了5个。这样做是为了创建更丰富的视觉体验。 通过以可变的速度移动某些图层,同时使其他图层保持静止,则会产生引人注目的视差效果。 下图演示了固定的大气层和4个滚动层,分别表示前景,中地面,背景和远处的背景。 另外,与物理地形相互作用的车辆进一步增强了场景的真实感。 在适当的地方,雪,尘土或发动机排气等颗粒效应可以提供更多的氛围。

Image for post
The 5-layer hierarchy of this demo’s parallax effect
该演示的视差效果的5层层次结构

平铺 (Tiling)

At the heart of this demo is the goal of endless scrolling. To efficiently render an infinite graphic environment, it has to be broken down into pages, i.e it has to be tiled. Tiling is a widely-used technique which enables scrolling more content than a device can display. The most common example of this on iOS is UITableView. UITableView arranges its cells one after the other to create a seamless list. Extending that concept to this project, each of the 4 moving layers of the parallax needs to be tiled such that the viewer cannot distinguish where one page begins or another ends. In this demo, the class responsible for this is ScrollingNode. It arranges page nodes one after the other to create a seamless sideways “list”:

该演示的核心是无限滚动的目标。 为了有效地渲染无限的图形环境,必须将其分解为页面,即必须将其平铺。 平铺是一种广泛使用的技术,它可以滚动显示比设备可以显示的内容更多的内容。 在iOS上最常见的示例是UITableViewUITableView排列其单元格以创建无缝列表。 将该概念扩展到该项目,需要对视差的4个移动层中的每一个进行平铺,以使观看者无法区分一页的开始位置或另一页的结束位置。 在此演示中,负责此操作的类是ScrollingNode 。 它一个接一个地排列页面节点,以创建一个无缝的横向“列表”:

Comparing tiling an endlessly scrolling environment with UITableView’s tiling
Rendering an endless environment borrows some concepts from UITableView
渲染无尽的环境需要借鉴UITableView的一些概念

Conceptually, this borrows the ideas of reusing and dequeueing from UITableView. Practically, it follows the techniques laid out in a WWDC session from 2011 (Advanced Scroll View Techniques) by the excellent Josh Shaffer and Eliza Block. In short, ScrollingNode creates a cache for recycled pages. Whenever a page has scrolled off-screen, it is recycled and made available for reuse. Since the maximum number of visible pages for any given layer is 2, only 2 are ever allocated. Some math determines the indexes of the pages that need to be displayed, and based on these indexes, ScrollingNode adds or removes pages to the hierarchy and positions them accordingly. When optimized, the tiling is imperceptible to the viewer and the result is a smooth, seamless scroll which can reliably go on indefinitely:

从概念上讲,这借鉴了UITableView的重用和出队思想。 实际上,它遵循了出色的Josh Shaffer和Eliza Block 从2011年开始在WWDC会议中提出的技术(高级滚动视图技术) 。 简而言之, ScrollingNode为回收的页面创建缓存。 每当页面在屏幕外滚动时,页面就会被回收并可供重复使用。 由于任何给定层的最大可见页面数是2,所以只分配了2。 一些数学方法确定需要显示的页面的索引,并基于这些索引, ScrollingNode将页面添加或删除到层次结构中,并相应地放置它们。 进行优化后,观看者无法察觉到平铺,结果是平滑,无缝的滚动,可以无限期可靠地进行下去:

Demo of multiple tiled layers
Simple 2D tiling can provide a smooth and seamless scrolling experience
简单的2D平铺可提供平滑无缝的滚动体验

This abbreviated version of ScrollingNode shows how the tiling is done using basic math to calculate where in the environment a page is supposed to be, and whether it should currently be displayed. Convenient Set methods move the pages between the visible and recycled caches. The pages themselves have no knowledge of their content (they are meant to be dumb containers), but they do need to be identified and compared, so they hold on to a pageIndex property. ScrollerNode’s sole responsibility is tiling. To populate the pages with actual graphics content, it delegates to a dedicated content provider, passing the page which needs graphics. This is not listed in the gist above, but is in the demo code.

ScrollingNode这个缩写版本显示了如何使用基本数学方法来完成平铺操作,以计算页面在环境中的位置以及当前是否应显示该页面。 方便的Set方法在可见缓存和回收缓存之间移动页面。 页面本身不知道其内容(它们本来是愚蠢的容器),但是确实需要进行标识和比较,因此它们保留了pageIndex属性。 ScrollerNode的唯一责任是平铺。 为了用实际的图形内容填充页面,它将委派给专门的内容提供者,传递需要图形的页面。 上面的要旨中没有列出该代码,但是在演示代码中。

图形 (Graphics)

To meet the bonus challenge of drawing all graphics procedurally, this demo utilizes SpriteKit’s built-in drawing capabilities. Specifically, SKShapeNode provides a convenient way to draw vector graphics at runtime. SKShapeNode uses CGPath objects to draw its content. In this demo, all the themes’ graphics are drawn using SKShapeNode. This was done by abstracting the intended artwork into more primitive shapes that can be expressed using points, lines and arcs. For example, the Desert theme’s cacti are really just line segments and curves integrated into the page’s overall path. They are each intended to evoke the look of a cactus plant, without the fine details that would burden the runtime performance. Similarly, the trees in the Alps theme are drawn by following a path which resembles a tree, then filling it with a solid color.

为了应对按程序绘制所有图形所带来的额外挑战,此演示利用了SpriteKit的内置绘制功能。 具体来说, SKShapeNode提供了一种在运行时绘制矢量图形的便捷方法。 SKShapeNode使用CGPath对象绘制其内容。 在此演示中,所有主题的图形都是使用SKShapeNode绘制的。 这是通过将预期的艺术品抽象为更原始的形状(可以使用点,线和弧表示)来完成的。 例如,“沙漠”主题的仙人掌实际上只是整合到页面整体路径中的线段和曲线。 它们各自旨在唤起仙人掌植物的外观,而没有会增加运行时性能的精致细节。 同样,阿尔卑斯山主题中的树木是通过沿着类似于树木的路径绘制的,然后用纯色填充。

Image for post
SKShapeNode SKShapeNode

As a performance optimization, entire pages of content are rendered as a single shape (where possible). In the figure above, a single CGPath contains both the terrain shape and the variable cacti which sit on top. This reduces the total number of nodes as well as draw calls. Because the tiling mechanism provides pages right before they are displayed, there can be no delay in rendering the content of each page. To maintain 60 FPS, 16 milliseconds is all the time available to prepare the page. Therefore simplifying the drawing code is key to ensuring the game renders smoothly.

作为性能优化,整个内容页面呈现为单个形状(如果可能)。 在上图中,单个CGPath包含地形形状和位于顶部的变量仙人掌。 这减少了节点以及绘图调用的总数。 由于平铺机制会在显示页面之前立即提供页面,因此呈现每个页面的内容不会有任何延迟。 为了维持60 FPS,准备页面始终需要16毫秒的时间。 因此,简化绘图代码是确保游戏流畅渲染的关键。

Procedural drawing code can be tedious and verbose. To avoid unnecessary noise and distraction, the drawing code in this demo has been abstracted away to a static library. But stay tuned for a future article focused on programmatic drawing and how it can be tweaked to generate unique art.

程序绘图代码可能是乏味且冗长的。 为了避免不必要的干扰和干扰,此演示中的绘图代码已抽象为静态库。 但是,请继续关注将来的文章,重点关注程序化绘图以及如何对其进行调整以生成独特的艺术。

The exception to the procedural drawing challenge is that the vehicles themselves presented visuals which were too complex to draw using code. Therefore, the vehicles are the only pre-rendered textures that are included in the project.

程序绘制挑战的一个例外是,车辆本身呈现的视觉效果过于复杂,无法使用代码进行绘制。 因此,车辆是项目中唯一包含的预渲染纹理。

车辆与物理 (Vehicles & Physics)

The theme-distinct vehicles were added to provide a representation of the player, or the game’s main character/object. They also act as a focal point for each theme. Together with the stationary atmospheric layer, they help achieve the parallax effect and provide a natural resting place for the viewer’s gaze.

添加了与主题截然不同的载具,以表示玩家或游戏的主要角色/对象。 它们还充当每个主题的焦点。 它们与静止的大气层一起,有助于实现视差效果,并为观看者的视线提供自然的休息场所。

Each vehicle is a composite of sprite nodes and corresponding physics bodies. It contains a chassis body, and an array of wheels. The wheels are fused to the chassis using physics joints (instances of SKPhysicsJointPin). This allows them to rotate freely while being attached to the chassis. SpriteKit offers an array of properties to tweak in order to achieve the desired physical characteristics. This demo sets the shape, mass, friction, restitution, and angular damping of each physics body.

每辆车都是精灵节点和相应的物理物体的组合。 它包含一个底盘主体和一系列车轮。 车轮通过物理关节(例如SKPhysicsJointPin )熔合到底SKPhysicsJointPin 。 这使它们在连接到机箱时可以自由旋转。 SpriteKit提供了一系列可调整的属性,以实现所需的物理特性。 该演示设置了每个物理物体的形状,质量,摩擦,恢复和角阻尼。

The movement of the vehicles is ultimately what controls the scrolling of the game environment. To make this feel as natural as possible, torque is applied to each vehicle’s wheel, spinning it. The friction settings between the wheels and the terrain are maximized, propelling the vehicle forward. Angular damping causes the vehicles to slow down quickly when not accelerating, and restitution controls how bouncy a vehicle behaves

车辆的运动最终是控制游戏环境滚动的因素。 为了使这种感觉尽可能自然,在每个车轮上都施加了扭矩,使车轮旋转。 车轮和地形之间的摩擦设置被最大化,从而使车辆前进。 角阻尼会导致车辆在不加速时Swift减速,并且恢复原状控制车辆的弹力

Image for post
Bouncy vs smooth vehicle physics
有弹性与平稳的车辆物理

Several factors affect the bounciness of a vehicle. In this demo’s Forest theme, the desired effect was to emulate the bouncy suspension of a jeep driven over a rough jungle terrain. To achieve this effect, the vehicle’s physics bodies were given a high restitution value. In addition, the chassis’s mass is set to a value much higher than the mass of the wheels, which causes it to apply more downward force to the wheels and ground, exaggerating the bounciness. By contrast, the vehicle on the right has a light chassis and no restitution in its physics bodies. The result is a much smoother travel dynamic, even though the terrain itself is just as rough as the vehicle on the left.

有几个因素会影响车辆的弹跳。 在此演示的“森林”主题中,所需的效果是模拟吉普车在崎jungle的丛林地形上行驶时的弹性悬挂。 为了达到这种效果,车辆的物理机体具有很高的复原价值。 另外,底盘的质量被设置为比车轮的质量高得多的值,这导致其向车轮和地面施加更大的向下力,从而夸大了弹跳力。 相比之下,右侧的车辆具有轻型底盘,并且其物理机体没有恢复原状。 即使地形本身与左边的车辆一样崎rough不平,结果还是可以使行驶动态更加顺畅。

性能 (Performance)

Working with a capable native 2D framework, it can be easy to get carried away with a game’s processing requirements. There is a limit – however – and the best way to make sure that a game doesn’t hit it is to diligently audit the game’s technical asks. The environmental mantra of Recycle / Reuse / Reduce is 100% applicable to game development (and software engineering at large), especially on mobile devices with limited resources. This demo applies these principles through its focus on traditional approaches to optimization:

使用功能强大的本机2D框架,可以轻松满足游戏的处理要求。 但是有一个限制,要确保游戏没有成功,最好的方法就是认真审核游戏的技术要求。 回收/再利用/减少的环境口号100%适用于游戏开发(以及整个软件工程),尤其是在资源有限的移动设备上。 本演示通过重点关注传统的优化方法来应用这些原则:

  • Reduce node count: As mentioned in the Tiling section above, the demo renders each endless layer using only 2 page nodes which are reused and tiled. This reduces the nodes required to a minimum.

    减少节点计数 :如在上面的平铺部分中提到,该演示呈现仅使用2,其被重新使用和瓷砖页节点每个无端层。 这将所需的节点减少到最少。

  • Reduce draw calls: As mentioned in the Graphics section above, drawing calls are minimized by combining terrain shapes with other shapes like trees. This offloads the heavy lifting to some CGPath math, allowing each page’s graphics to be rendered using a single SKShapeNode.

    减少绘制调用 :如以上“ 图形”部分所述,通过将地形形状与其他形状(例如树木)结合起来,可以最大程度地减少绘制调用。 这CGPath了一些CGPath数学的繁重工作,从而允许使用单个SKShapeNode呈现每个页面的图形。

  • Reduce calculations in update(_:): The update(_:) method is called every frame while the scene is running. As such, it is especially susceptible to performance bottlenecks. This demo limits update(_:) code to just managing the camera’s tracking of the vehicle as it traverses the environment.

    减少 update(_:) 计算 :场景运行时每帧调用一次update(_:)方法。 因此,它特别容易受到性能瓶颈的影响。 此演示将update(_:)代码限制为仅管理摄像机穿越环境时对车辆的跟踪。

  • Preload textures: While SKTexture provides preloading functionality, it doesn’t help when drawing nodes procedurally. This demo provides its own preloading by pre-drawing SKShapeNode objects, rasterizing and caching them in a bank, reducing overhead during runtime.

    预加载纹理 :虽然SKTexture提供了预加载功能,但在按程序绘制节点SKTexture 。 该演示通过预先绘制SKShapeNode对象,将它们光栅化和缓存到库中来提供自己的预加载,从而减少了运行时的开销。

  • Simplify physics bodies: SKPhysicsBody objects have an inversely-proportional relationship between their accuracy and performance. This demo chooses a pragmatic middle ground on that spectrum. For example, the vehicles’ physics bodies don’t correspond 1:1 to the vehicles’ pixel-level visual appearance. But they don’t represent a circle either (the most efficient body shape). Instead, they represent a rectangular shape which approximates the general look of the vehicle, providing a physical dynamic that is just realistic enough to convince the viewer.

    简化物理 SKPhysicsBodySKPhysicsBody对象的准确性和性能之间具有反比例关系。 该演示在该频谱上选择了务实的中间立场。 例如,车辆的物理物体与车辆的像素级视觉外观并不1:1。 但是它们也不代表圆形(最有效的身体形状)。 取而代之的是,它们代表近似于车辆总体外观的矩形形状,从而提供了足以逼真的观察者说服力的物理动力。

代码结构 (Code Structure)

This demo consists of 2 groups of objects:

该演示包含2组对象:

A core, reusable group which produces the endless scrolling environment, These objects are the same for every theme:

一个可重复使用的核心组,可产生无穷无尽的滚动环境,这些对象对于每个主题都是相同的:

  • View Controller: Composes the root SKView, SKSceneobjects and tutorial. Also adds UI for changing themes and creates new SKScene objects when the theme changes

    视图控制器 :组成根SKViewSKScene对象和教程。 还添加用于更改主题的UI并在主题更改时创建新的SKScene对象

  • GameScene: Instantiated with a theme object. Constructs the parallax layers, the vehicle node, the theme’s optional particle effects and the UI for the accelerator. Also handles user touches

    GameScene :用主题对象实例化。 构造视差层,载具节点,主题的可选粒子效果和加速器的UI。 同时处理用户触摸

  • ScrollingNode: Handles tiling of pages to create the illusion of one long, continuous scrolling environment

    ScrollingNode :处理页面的平铺以创建一种长时间连续滚动环境的错觉

The second group provides theme-specific content:

第二组提供特定于主题的内容:

  • Theme: This enum encapsulates all the data specific to a theme, including its content provider, vehicle, particle effects and more

    主题 :此枚举封装了特定于主题的所有数据,包括其内容提供者,载具,粒子效果等

  • Content Provider: This static library works together with ScrollingNode to match up theme graphics for each page that is being

    内容提供程序 :此静态库与ScrollingNode一起使用,以匹配正在创建的每个页面的主题图形

  • Vehicle: This SKNode subclass constructs the body of the vehicle, and provides the physical traits that dictate its behavior as it traverses the terrain

    车辆 :此SKNode子类构造车辆的主体,并提供决定其在穿越地形时的行为的物理特征。

下一步是什么? (What’s Next?)

To push this project further, some things to consider might be to add some actual gaming elements (e.g dodging obstacles, killing enemies, gaining points…etc.). There are more scrolling questions to answer (e.g how can the environment be moved vertically to create more interesting gameplay? How can the camera zoom out to show more of the environment around the vehicle?). There is additional performance to squeeze out of the code, like utilizing dynamic run-time asset catalogs populated with the procedurally-drawn graphic assets. And last but not least, there are some bugs to fix, like the issue of physics joints losing their tightness after some time:

为了进一步推进该项目,需要考虑的一些事情是添加一些实际的游戏元素(例如,躲避障碍,杀死敌人,获得积分等)。 还有更多滚动问题要回答(例如,如何垂直移动环境以创建更多有趣的游戏玩法?相机如何缩小以显示车辆周围的更多环境?)。 可以挤出代码来提高性能,例如利用以程序绘制的图形资产填充的动态运行时资产目录。 最后但并非最不重要的一点是,有一些错误需要修复,例如物理关节在一段时间后失去紧密性的问题:

Image for post
After scrolling about 10,000 pages, this rear wheel physics joint is not what it used to be 😯
滚动大约10,000页后,此后轮物理关节不再是以前的样子😯

Overall, I hope this project demonstrates how some simple Swift code – coupled with SpriteKit – can create a convincing parallax-based, infinitely scrolling environment. When combined with a player object and some basic 1-touch controls, a game-like experience can be produced, laying a foundation for a 2D game which can humbly be categorized as an “Infinite Scroller”

总的来说,我希望这个项目能够演示一些简单的Swift代码以及SpriteKit如何创建令人信服的基于视差的无限滚动环境。 当与玩家对象和一些基本的一键式控件结合使用时,就可以产生类似游戏的体验,从而为2D游戏奠定了基础,后者可以被归类为“无限滚动器”

翻译自: https://medium.com/@AhmedFarghaly/anatomy-of-a-scroller-6f811543e49d

内层滚动条随外层滚动条滚动

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2021 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值