我敢肯定这之前已经解决了 1000 次:我有一个大小为 960*560 的画布和一个大小为 5000*3000 的房间,其中始终只应绘制 960*560,具体取决于玩家所在的位置。玩家应该总是在中间,但是当靠近边界时——那么最好的视角应该被计算出来)。玩家可以使用 WASD 或箭头键完全自由移动。所有的物体都应该自己移动——而不是我移动除了玩家之外的所有其他东西来创造玩家移动的错觉。
我现在发现了这两个问题:
HTML5 - 为画布创建视口是可行的,但仅适用于此类游戏,我无法为我的游戏重现代码。
更改 html5 画布的视图“中心” 似乎更有前途且性能更好,但我只理解它是为了相对于播放器正确绘制所有其他对象,而不是如何相对于播放器滚动画布视口,这是我想要的首先当然要实现。
我的代码(简化 - 游戏逻辑是分开的):
var canvas = document.getElementById("game");
canvas.tabIndex = 0;
canvas.focus();
var cc = canvas.getContext("2d");
// Define viewports for scrolling inside the canvas
/* Viewport x position */ view_xview = 0;
/* Viewport y position */ view_yview = 0;
/* Viewport width */ view_wview = 960;
/* Viewport height */ view_hview = 560;
/* Sector width */ room_width = 5000;
/* Sector height */ room_height = 3000;
canvas.width = view_wview;
canvas.height = view_hview;
function draw()
{
clear();
requestAnimFrame(draw);
// World's end and viewport
if (player.x < 20) player.x = 20;
if (player.y < 20) player.y = 20;
if (player.x > room_width-20) player.x = room_width-20;
if (player.y > room_height-20) player.y = room_height-20;
if (player.x > view_wview/2) ... ?
if (player.y > view_hview/2) ... ?
}
我试图让它工作的方式感觉完全错误,我什至不知道我是如何尝试的……有什么想法吗?您如何看待 context.transform-thing?
我希望你能理解我的描述,并且有人有想法。亲切的问候
原文由 user2337969 发布,翻译遵循 CC BY-SA 4.0 许可协议
在 jsfiddle.net 进行 现场演示
该演示演示了真实游戏场景中视口的使用。使用箭头键将玩家移动到房间上方。大房间是使用矩形动态生成的,结果保存到图像中。
请注意,玩家总是在中间,除非靠近边界(如您所愿)。
现在,我将尝试解释代码的主要部分,至少是那些光看就更难理解的部分。
使用drawImage根据视口位置绘制大图
drawImage 方法的一个变体有八个新参数。我们可以使用此方法对源图像的部分进行切片并将它们绘制到画布上。
与其他变体一样,第一个参数图像是对图像对象的引用或对不同画布元素的引用。对于其他八个参数,最好查看下图。前四个参数定义切片在源图像上的位置和大小。最后四个参数定义目标画布上的位置和大小。
字体: https ://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Using_images
它在演示中是如何工作的:
我们有一个代表房间的大图像,我们只想在画布上显示视口内的部分。裁剪位置(sx,sy)与相机(xView,yView)的位置相同,裁剪尺寸与视口(画布)相同,因此
sWidth=canvas.width
和sHeight=canvas.height
。我们需要注意裁剪尺寸,因为
drawImage
如果裁剪位置或基于位置的裁剪尺寸无效,则不会在画布上绘制任何内容。这就是为什么我们需要下面的if
部分。绘制与视口相关的游戏对象
编写游戏时,最好将游戏中每个对象的逻辑和渲染分开。所以在演示中我们有
update
和draw
函数。update
方法更改对象状态,如在“游戏世界”中的位置、应用物理、动画状态等draw
方法实际渲染对象并考虑视口正确渲染它,对象需要知道渲染上下文和视口属性。请注意,游戏对象会根据游戏世界的位置进行更新。这意味着对象的 (x,y) 位置是世界中的位置。尽管如此,由于视口在变化,因此需要正确渲染对象,并且渲染位置将不同于世界位置。
转换很简单:
物体在世界(房间)中的位置:
(x, y)
视口位置:
(xView, yView)
渲染位置:
(x-xView, y-yView)
这适用于所有类型的坐标,甚至是负坐标。
游戏相机
我们的游戏对象有一个单独的更新方法。在 Demo 实现中,相机被视为游戏对象,也有单独的更新方法。
相机对象持有视口的左上角位置
(xView, yView)
,一个要跟随的对象,一个代表视口的矩形,一个代表游戏世界边界的矩形和玩家之前可以到达的每个边界的最小距离相机开始移动 (xDeadZone, yDeadZone)。我们还定义了相机的自由度(轴)。对于顶视图风格的游戏,如角色扮演游戏,允许相机在 x(水平)和 y(垂直)轴上移动。为了让玩家保持在视口的中间,我们将每个轴的死区设置为与画布的中心会聚。查看代码中的以下函数:
注意:请参阅下面的更新部分,因为当地图(房间)的任何维度小于画布时,这不会产生预期的行为。
世界的极限
由于包括相机在内的每个对象都有自己的更新功能,因此很容易检查游戏世界的边界。只记得将阻止移动的代码放在更新函数的最后。
示范
查看完整代码并自己尝试。大部分代码都有注释来指导您完成。我假设您了解 Javascript 的基础知识以及如何使用原型(有时我使用术语“类”来表示原型对象只是因为它与 Java 等语言中的类具有相似的行为)。
现场演示
完整代码:
更新
如果地图(房间)的宽度和/或高度小于画布,则之前的代码将无法正常工作。要解决此问题,请在游戏脚本中按如下方式设置相机:
您只需要告诉相机构造函数视口将是地图(房间)或画布之间的最小值。由于我们希望玩家居中并绑定到该视口,因此
camera.follow
函数也必须更新。请随时报告任何错误或添加建议。