在2D游戏中绘制等距图块的正确方法是什么?
我已经阅读了一些参考文献(例如这篇文章),它们建议以一种在地图的二维数组表示中对每一列进行锯齿形的方式渲染图块.我想它们应该以钻石方式绘制得更多,其中被绘制到屏幕上的内容更接近于2D阵列的外观,只是旋转了一点.
两种方法都有优缺点吗?
我正在开发一款支持快速浏览器的等距游戏,<canvas>非常有趣.为了保存每个图块的信息,我使用一个二维数组,其中包含代表图块ID的数字,如:
var level = [[1, 1, 1, 2, 1, 0],
[0, 1, 1, 2, 0, 1],
[0, 1, 1, 2, 1, 1]];
var tiles = [
{name: 'grass', color: 'green'},
{name: 'water', color: 'blue'},
{name: 'forest', color: 'ForestGreen'}
];
Run Code Online (Sandbox Code Playgroud)
到目前为止,它的效果很好,但现在我想在这张照片中使用高度和斜率: alt text http://harmen.no-ip.org/isometrictiles.png
对于每个瓷砖,我需要保存它的瓷砖ID,高度和有关哪些角向上翻转的信息.
我想出了一个关于所有四个角的按位表示的简单想法,如下所示:
1011 // top, bottom and left corner turned up
Run Code Online (Sandbox Code Playgroud)
我的问题是:为每个单元格保存这三个值的最有效方法是什么?是否可以将这三个值保存为一个整数?
Struggeling将鼠标的位置转换为网格中tile的位置.当它完全平坦时,数学看起来像这样:
this.position.x = Math.floor(((pos.y - 240) / 24) + ((pos.x - 320) / 48));
this.position.y = Math.floor(((pos.y - 240) / 24) - ((pos.x - 320) / 48));
Run Code Online (Sandbox Code Playgroud)
其中pos.x和pos.y是鼠标的位置,240和320是贴片的大小,24和48是贴片的大小.然后,位置包含我正在悬停的图块的网格坐标.这在平坦表面上工作得相当好.

现在我正在增加高度,数学没有考虑到.

此网格是包含噪声的2D网格,正在转换为高度和平铺类型.高度实际上只是对瓷砖"Y"位置的调整,因此可以在同一位置绘制两个瓷砖.
我不知道如何确定我在哪个瓷砖上空盘旋.
编辑:
取得了一些进展...之前,我依靠鼠标悬停事件来计算网格位置.我只是将其更改为在绘制循环本身中进行计算,并检查坐标是否在当前绘制的图块的限制范围内.创造了一些头顶的东西,不确定我是否对它非常满意,但我会确认它是否有效.
编辑2018:
我没有答案,但既然这是[sd]开放的赏金,请自己修改一些代码和演示
网格本身是简化的;
let grid = [[10,15],[12,23]];
Run Code Online (Sandbox Code Playgroud)
这导致如下绘图:
for (var i = 0; i < grid.length; i++) {
for (var j = 0; j < grid[0].length; j++) {
let x = (j - i) * resourceWidth;
let y = ((i + j) …Run Code Online (Sandbox Code Playgroud) 我不使用tile,而是使用sf :: Vertex绘制的多维数据集.每个立方体有6个边,每个边有4个点.

所以我只需要cubes[numCube].sides()[numSide]....选择一个方面.
我创建了立方体layer.cpp:
for(int J = 0; J < mapSize; J++)
{
for(int I = 0; I < mapSize; I++)
{
x = (J - I) * (cubeSize/2);
y = (J + I) * (cubeSize/4);
c = new cube(cubeSize, x, y, z, I, J);
cs.push_back(*c);
}
}
Run Code Online (Sandbox Code Playgroud)
在cube.cpp中我创建了侧面,然后,在sides.cpp中,我像这样计算每个点的坐标:
switch(typeSide)
{
case 0://DOWN_SIDE
light = 1;
tmp_x = x + (size/2);
tmp_y = y + (size/2);
p0 = new point(tmp_x, tmp_y, tmp_z);
tmp_x = x + size; …Run Code Online (Sandbox Code Playgroud) 我是使用C++进行OpenGL编程的新手,并不是很擅长数学.是否有一种简单的方法来进行等角投影?
我指的是真正的等距投影,而不是一般的正交投影.
(等距投影仅在单位X,Y和Z矢量的投影长度相等且它们之间的角度恰好为120度时发生.)
代码片段受到高度赞赏..

我有一个等距网格系统,坐标从网格左上角的[0,0]开始(上图中显示的角落),x向图像底部递增,y向顶部递增(所以[ 0,height]将是顶角,[width,0]将是菱形的底角,宽度和高度是网格的大小,即200 x 200正方形)
无论如何我需要帮助的是获得一组等距网格位置,这些位置包含在图像中显示的蓝色框中.没有遍历每个x,y屏幕pos并获得相应的网格位置(请参阅我之前提到的关于如何从屏幕位置转换为网格位置的问题获取等距网格上的行/列.)我不知道如何有效地实现这一目标.
我之前发现的一个问题就是这里几乎完全相同的链接.答案是将网格渲染为每个网格方块的不同颜色的图像,然后检测广场下面的颜色,我已经实现了这个解决方案,但它很慢!我几乎在考虑检查选择框中每个像素的网格位置会更快.为什么哦为什么javascript循环这么慢!
我真的需要一个基于我的坐标系统来解决这个问题的数学解决方案,但我似乎无法想出一些有效的东西(并且处理选择框离开网格)
如果您需要更多信息,请与我们联系.
编辑:不幸的是,提供的答案到目前为止还没有工作,因为选择就像在正方形网格上有一个钻石选择区域,除非我错过了答案的点,否则实际上没有左上角,右下角要迭代?我已经优化了渲染方法,但是在大量选择中,它仍会在帧中添加明显的下降,因为它遍历所有像素检查颜色并获得相应的方块
在我的2D等距引擎中,我有以下类:
`maps(variable)/layers(variable)/cubes(variable)/sides(6)/points(4)/coordinates(3)`
Run Code Online (Sandbox Code Playgroud)
sides包含4 points(1 point= 1 coordinate(x,y,z)).Folers:
assets/numTexture/numLight.png
Run Code Online (Sandbox Code Playgroud)
我用numTexture和numLight计算一个数字,它是textureNumberEntry(我在数组中加载了所有numLight.png(纹理)).
但纹理化的错误是错误的:
我在图层类中定义了立方体坐标:
for(int J = 0; J < mapSize; J++)
{
for(int I = 0; I < mapSize; I++)
{
x = (J - I) * (cubeSize/2);
y = (J + I) * (cubeSize/4);
c = new cube(cubeSize, x, y, z, I, J);
cs.push_back(*c);
}
}
Run Code Online (Sandbox Code Playgroud)
在side.cpp中,我在sideType上有一个开关(如果它是顶部,左边等等......我用不同的方式定义我的点'坐标).每个立方体我有6个(这里只有数据)像这样:
switch(typeSide)
{
case 0://DOWN_SIDE
light = 0;
tmp_x = x + (size/2);
tmp_y = y + …Run Code Online (Sandbox Code Playgroud) 我正在考虑应用此解决方案的游戏,是一个可以更改切片的游戏,也可以是编辑器.您将需要为两种类型的墙壁制作图形:朝西,朝东或北/西.
在等距瓷砖游戏中似乎有两种方法可以使用墙壁:
示例图

第二种方法似乎是最简单的方法.由于墙壁占据整个瓷砖,您不必担心与floortile重叠并且必须围绕它设计图案.如果你想制作与网格不同的图案,你会遇到另一个问题.就像你想拥有相当苗条的墙壁一样.您的网格也需要更大.在正面,只需要1个墙壁.
第一个方法需要一些代码来检测墙上的变化以进行绘制,除非围绕它设计地砖,否则可能看起来有些奇怪.它还需要至少4件.
哪种方法是首选的方法,为什么?
我正在研究等距游戏引擎,并且已经为像素完美点击检测创建了算法.访问该项目并注意到点击检测能够检测到点击了哪个边缘.它还会检查y-index以单击最前面的磁贴.
等距网格由100*65px的平铺图像组成.
TileW=100, TileL=50, tileH=15
地图由三维数组表示map[z][y][x].
平铺中心点(x,y)的计算如下:
//x, y, z are the position of the tile
if(y%2===0) { x-=-0.5; } //To accommodate the offset found in even rows
this.centerX = (x*tileW) + (tileW/2);
this.centerY = (y*tileL) - y*((tileL)/2) + ((tileL)/2) + (tileH/2) - (z*tileH);
Run Code Online (Sandbox Code Playgroud)
用于确定鼠标是否位于磁贴上给定区域内的原型函数:
Tile.prototype.allContainsMouse = function() {
var dx = Math.abs(mouse.mapX-this.centerX),
dy = Math.abs(mouse.mapY-this.centerY);
if(dx>(tileW/2)) {return false;} //Refer to image
return (dx/(tileW*0.5) + (dy/(tileL*0.5)) < (1+tileHLRatio));
}
Run Code Online (Sandbox Code Playgroud)
Tile.prototype.allContainsMouse()如果鼠标在绿色范围内,则返回true.通过检查dx是瓷砖宽度的一半来裁剪红色区域
Tile.prototype.topContainsMouse = …Run Code Online (Sandbox Code Playgroud) 我是Phaser的新手,我现在很难在相位器等距插件的帮助下生成一个tilemap.我也很难理解与Phaser世界,游戏和相机相关的一些概念,这些概念使得更正确地生成tilemap的一部分.我实际上已经注意到这个问题似乎是Phaser新手的障碍,就像我自己一样,正确解释它肯定有助于对抗这一点.
谈到此事:
我使用单个草精灵使用for循环生成了一个tilemap .for循环工作正常,我还实现了一个函数,我可以指定我想要生成的tile数.
{
spawnBasicTiles: function(half_tile_width, half_tile_height, size_x, size_y) {
var tile;
for(var xx = 0; xx < size_x; xx += half_tile_width) {
for(var yy = 0; yy < size_y; yy += half_tile_height) {
tile = game.add.isoSprite(xx, yy, 0, 'tile', 0, tiles_group);
tile.checkWorldBounds = true;
tile.outOfBoundsKill = true;
tile.anchor.set(0.5, 0);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
因此,生成静态草砖的过程不是问题.问题,以及我试图让对象池正常工作的原因之一是当磁贴数量超过80时,这对游戏性能产生了巨大影响.由于我的目标是制作巨大的自动生成地图,这些地图根据玩家角色位置进行渲染,因此对象池是必不可少的.我已经为这些图块创建了一个组,并添加了我认为将不需要渲染的图块(物理,世界边界......)所需的属性.然而,经过多次尝试,我得出结论,即使是超出界限的瓷砖仍然会产生.我还使用了另一个属性而不是add.isoSprite来生成tile,这是.create但没有区别.我想没有瓷砖被"杀死".
除此之外,我希望在世界中间生成我的角色,这是生成的草地图的中间.但是,我也很难做到这一点.我认为,为了实现这一目标,我应该使用以下概念,尽管不能:
总结: 使用以下用于生成切片的循环方法,如何在跟随我的角色的相机移动到另一个区域时生成无限/几乎无限的地图?除此之外,在我生成的草地图中间始终生成角色的正确方法是什么,以及相机从角色的起点开始?
我将非常感谢他们的帮助,并希望这对于处于类似情况的人来说可能是有价值的.对于任何额外的信息,只需询问,如果您想要按特定数字生成图块的功能,我很乐意发布.对不起任何语言错误.
非常感谢你.
编辑:
通过将X和Y设置为(大小/宽度)和(大小/高度),我能够始终在草中间生成我的角色.大小是px中X和Y的度量.