16 진수 맵에 대해 픽셀 대 좌표 기능을 만들려고하는데 수학을 올바르게 얻지 못합니다. 내가 시도하는 모든 것이 조금 벗어난 것처럼 보였으며 찾은 예제는 원 중심 맵을 기반으로합니다.
‘배열 기반’이란 육각형을 주문하는 방식을 의미합니다 (그림 참조).
내가 얻은 가장 정확한 결과는 다음 코드를 사용했지만 여전히 꺼져 있으며 값이 높아질수록 악화됩니다.
public HexCell<T> coordsToHexCell(float x, float y){
final float size = this.size; // cell size
float q = (float) ((1f/3f* Math.sqrt(3) * x - 1f/3f * y) / size);
float r = 2f/3f * y / size;
return getHexCell((int) r, (int) q);
}
화면은 왼쪽 상단에 0,0으로 시작하며 각 셀은 중앙을 알고 있습니다.
필요한 것은 화면 좌표를 16 진 좌표로 변환하는 방법입니다. 내가 어떻게 할 수 있습니까?
답변
16 진 좌표계가 많이 있습니다. “오프셋”접근 방식은 직사각형 맵을 저장하는 데 유용하지만 16 진 알고리즘은 더 까다로운 경향이 있습니다.
내에서 진수 그리드 가이드 (난 당신이 이미 발견했습니다 생각하는) 당신의 좌표계가있는 거 라벨을 당신을 제외하고, “심지어-R”이라고 r,q
대신 q,r
. 다음 단계에 따라 픽셀 위치를 16 진 좌표로 변환 할 수 있습니다.
- 이 섹션에서 설명하는 알고리즘을 사용하여 픽셀 위치를 축 16 진 좌표 로 변환 합니다 . 이것이 당신의 기능입니다. 그러나 한 단계 더 나아가 야합니다.
- 이러한 축 좌표는 분수입니다. 가장 가까운 16 진수로 반올림해야합니다. 코드에서 사용
(int)r, (int)q
하지만 사각형에만 작동합니다. 육각형의 경우 더 복잡한 반올림 접근법이 필요합니다. 변환r, q
에 큐브 사용하여 좌표 큐브에 축 공식 여기를 . 그런 다음 여기hex_round
기능을 사용 하십시오 . - 이제 큐브 좌표 의 정수 세트가 있습니다 . 지도는 큐브가 아닌 ‘even-r’을 사용하므로 다시 변환해야합니다. 이 큐브를 사용하여 공식 을 짝수 오프셋 하십시오 .
픽셀을 16 진수 좌표 섹션으로 다시 작성하여 훨씬 명확하게해야합니다. 죄송합니다!
나는 이것이 복잡한 것처럼 보인다. 이 방법은 오류가 발생하기 쉽고 (특별한 경우는 없습니다!) 재사용이 가능하기 때문에이 방법을 사용합니다. 이러한 변환 루틴을 재사용 할 수 있습니다. 16 진수 반올림을 재사용 할 수 있습니다. 16 진 좌표를 중심으로 선을 그리거나 회전하거나 시야각 또는 기타 알고리즘을 수행하려는 경우 이러한 루틴 중 일부가 유용합니다.
답변
제 생각에는이 문제를 처리하는 두 가지 방법이 있습니다.
-
더 나은 좌표계를 사용하십시오. 육각형에 숫자를 매기는 방법이 영리하다면 수학을 훨씬 쉽게 할 수 있습니다. Amit Patel은 6 각형 그리드에 대한 명확한 참조 를 가지고 있습니다. 해당 페이지 에서 축 좌표 를 찾고 싶을 것입니다 .
-
이미 해결 한 사람으로부터 코드를 빌리십시오. 내가 가진 몇 가지 코드를 그 난에서 해제 작품, 전투 Wesnoth을위한 소스. 내 버전에는 육각형의 평평한 부분이 있으므로 x와 y를 바꿔야합니다.
답변
Michael Kristofik의 대답 은 특히 Amit Patel의 웹 사이트를 언급 한 것이 맞다고 생각 하지만 초보자 접근 방식을 Hex 그리드에 공유하고 싶었습니다.
이 코드는 JavaScript에 대한 관심을 잃고 작성하지 않은 프로젝트에서 가져온 것이지만 16 진수 타일의 마우스 위치는 훌륭했습니다. 필자는 이 GameDev 기사 *를 참고 용으로 사용했습니다. 이 웹 사이트에서 저자는 이 이미지 를 사용하여 모든 16 진수 측면과 위치를 수학적으로 표현하는 방법을 보여주었습니다.
내 렌더 클래스에서 원하는 16 진수 길이를 설정할 수있는 메소드로 정의했습니다. 이 값 중 일부는 픽셀 대 16 진 좌표 코드에서 참조되었으므로 여기에 표시됩니다.
this.s = Side; //Side length
this.h = Math.floor(Math.sin(30 * Math.PI / 180) * this.s);
this.r = Math.floor(Math.cos(30 * Math.PI / 180) * this.s);
this.HEXWIDTH = 2 * this.r;
this.HEXHEIGHT = this.h + this.s;
this.HEXHEIGHT_CENTER = this.h + Math.floor(this.s / 2);
마우스 입력 클래스에서 화면 x 및 y 좌표를 허용하고 픽셀이있는 16 진수 좌표를 가진 객체를 반환하는 메서드를 만들었습니다. * 가짜 “카메라”가있어서 렌더링 위치에 대한 오프셋도 포함되어 있습니다.
ConvertToHexCoords:function (xpixel, ypixel) {
var xSection = Math.floor(xpixel / ( this.Renderer.HEXWIDTH )),
ySection = Math.floor(ypixel / ( this.Renderer.HEXHEIGHT )),
xSectionPixel = Math.floor(xpixel % ( this.Renderer.HEXWIDTH )),
ySectionPixel = Math.floor(ypixel % ( this.Renderer.HEXHEIGHT )),
m = this.Renderer.h / this.Renderer.r, //slope of Hex points
ArrayX = xSection,
ArrayY = ySection,
SectionType = 'A';
if (ySection % 2 == 0) {
/******************
* http://www.gamedev.net/page/resources/_/technical/game-programming/coordinates-in-hexagon-based-tile-maps-r1800
* Type A Section
*************
* * *
* * * *
* * * *
* * * *
*************
* If the pixel position in question lies within the big bottom area the array coordinate of the
* tile is the same as the coordinate of our section.
* If the position lies within the top left edge we have to subtract one from the horizontal (x)
* and the vertical (y) component of our section coordinate.
* If the position lies within the top right edge we reduce only the vertical component.
******************/
if (ySectionPixel < (this.Renderer.h - xSectionPixel * m)) {// left Edge
ArrayY = ySection - 1;
ArrayX = xSection - 1;
} else if (ySectionPixel < (-this.Renderer.h + xSectionPixel * m)) {// right Edge
ArrayY = ySection - 1;
ArrayX = xSection;
}
} else {
/******************
* Type B section
*********
* * * *
* * *
* * *
*********
* If the pixel position in question lies within the right area the array coordinate of the
* tile is the same as the coordinate of our section.
* If the position lies within the left area we have to subtract one from the horizontal (x) component
* of our section coordinate.
* If the position lies within the top area we have to subtract one from the vertical (y) component.
******************/
SectionType = 'B';
if (xSectionPixel >= this.Renderer.r) {//Right side
if (ySectionPixel < (2 * this.Renderer.h - xSectionPixel * m)) {
ArrayY = ySection - 1;
ArrayX = xSection;
} else {
ArrayY = ySection;
ArrayX = xSection;
}
} else {//Left side
if (ySectionPixel < ( xSectionPixel * m)) {
ArrayY = ySection - 1;
ArrayX = xSection;
} else {
ArrayY = ySection;
ArrayX = xSection - 1;
}
}
}
return {
x:ArrayX + this.Main.DrawPosition.x, //Draw position is the "camera" offset
y:ArrayY + this.Main.DrawPosition.y
};
},
마지막으로 렌더링의 디버그가 설정된 프로젝트의 스크린 샷입니다. 코드가 16 진수 좌표 및 셀 개요와 함께 TypeA 대 TypeB 셀을 검사하는 빨간색 선을 보여줍니다
. 희망이 있습니다.
답변
실제로 16 진수 수학이없는 솔루션을 찾았습니다.
이 질문에서 언급했듯이 각 셀은 픽셀 좌표에 가장 가까운 16 진수 중심을 계산하여 자신의 중심 좌표를 저장합니다. 픽셀 정밀도로 해당 16 진수 셀을 결정할 수 있습니다 (또는 매우 가깝습니다).
나는 그것이 각 셀을 반복해야하기 때문에 그것을하는 가장 좋은 방법이라고 생각하지 않으며 그것이 어떻게 세금을 부과 할 수 있는지 알 수 있지만 코드를 대체 솔루션으로 남겨 둘 것입니다.
public HexCell<T> coordsToHexCell(float x, float y){
HexCell<T> cell;
HexCell<T> result = null;
float distance = Float.MAX_VALUE;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
cell = getHexCell(r, c);
final float dx = x - cell.getX();
final float dy = y - cell.getY();
final float newdistance = (float) Math.sqrt(dx*dx + dy*dy);
if (newdistance < distance) {
distance = newdistance;
result = cell;
}
}
}
return result;
}
답변
다음은 Amit Patel 웹 사이트에 게시 된 기술 중 하나를 C #으로 구현 한 것입니다 (Java로 번역하는 것은 쉽지 않습니다).
public class Hexgrid : IHexgrid {
/// <summary>Return a new instance of <c>Hexgrid</c>.</summary>
public Hexgrid(IHexgridHost host) { Host = host; }
/// <inheritdoc/>
public virtual Point ScrollPosition { get { return Host.ScrollPosition; } }
/// <inheritdoc/>
public virtual Size Size { get { return Size.Ceiling(Host.MapSizePixels.Scale(Host.MapScale)); } }
/// <inheritdoc/>
public virtual HexCoords GetHexCoords(Point point, Size autoScroll) {
if( Host == null ) return HexCoords.EmptyCanon;
// Adjust for origin not as assumed by GetCoordinate().
var grid = new Size((int)(Host.GridSizeF.Width*2F/3F), (int)Host.GridSizeF.Height);
var margin = new Size((int)(Host.MapMargin.Width * Host.MapScale),
(int)(Host.MapMargin.Height * Host.MapScale));
point -= autoScroll + margin + grid;
return HexCoords.NewCanonCoords( GetCoordinate(matrixX, point),
GetCoordinate(matrixY, point) );
}
/// <inheritdoc/>
public virtual Point ScrollPositionToCenterOnHex(HexCoords coordsNewCenterHex) {
return HexCenterPoint(HexCoords.NewUserCoords(
coordsNewCenterHex.User - ( new IntVector2D(Host.VisibleRectangle.Size.User) / 2 )
));
}
/// <summary>Scrolling control hosting this HexGrid.</summary>
protected IHexgridHost Host { get; private set; }
/// <summary>Matrix2D for 'picking' the <B>X</B> hex coordinate</summary>
Matrix matrixX {
get { return new Matrix(
(3.0F/2.0F)/Host.GridSizeF.Width, (3.0F/2.0F)/Host.GridSizeF.Width,
1.0F/Host.GridSizeF.Height, -1.0F/Host.GridSizeF.Height, -0.5F,-0.5F); }
}
/// <summary>Matrix2D for 'picking' the <B>Y</B> hex coordinate</summary>
Matrix matrixY {
get { return new Matrix(
0.0F, (3.0F/2.0F)/Host.GridSizeF.Width,
2.0F/Host.GridSizeF.Height, 1.0F/Host.GridSizeF.Height, -0.5F,-0.5F); }
}
/// <summary>Calculates a (canonical X or Y) grid-coordinate for a point, from the supplied 'picking' matrix.</summary>
/// <param name="matrix">The 'picking' matrix</param>
/// <param name="point">The screen point identifying the hex to be 'picked'.</param>
/// <returns>A (canonical X or Y) grid coordinate of the 'picked' hex.</returns>
static int GetCoordinate (Matrix matrix, Point point){
var pts = new Point[] {point};
matrix.TransformPoints(pts);
return (int) Math.Floor( (pts[0].X + pts[0].Y + 2F) / 3F );
}
위에서 언급 한 MatrixInt2D 및 VectorInt2D 클래스를 포함하여 나머지 프로젝트는 여기에서 오픈 소스로 사용할 수 있습니다.
http://hexgridutilities.codeplex.com/
위의 구현은 평평한 육각형을위한 것이지만 HexgridUtilities 라이브러리에는 그리드를 전치하는 옵션이 포함되어 있습니다.
답변
일반 바둑판과 동일한 논리를 사용하는 간단한 대안 방법을 찾았습니다. 모든 타일의 중심과 모든 정점에서 점으로 격자에 스냅 효과를 만듭니다 (더 단단한 격자를 만들고 교대로 점을 무시 함).
이 접근 방식은 플레이어가 타일 및 정점과 상호 작용하는 Catan과 같은 게임에는 효과적이지만 플레이어가 타일과 상호 작용하는 게임에는 적합하지 않습니다. 육각형 타일이 아닌 좌표에 가장 가까운 중심점 또는 정점을 반환하기 때문입니다. 좌표가 있습니다.
기하학
타일 너비의 4 분의 1 열과 타일 높이의 절반 인 행이있는 그리드에 점을 배치하면 다음 패턴이 나타납니다.
그런 다음 바둑판 패턴 (skip if column % 2 + row % 2 == 1
)의 두 번째 점을 건너 뛰도록 코드를 수정하면 다음 패턴으로 끝납니다.
이행
이 지오메트리를 염두에두고, 정사각형 그리드와 마찬가지로 2D 배열을 만들어 그리드의 x, y
각 점에 대한 좌표 (첫 번째 다이어그램에서)를 다음과 같이 저장할 수 있습니다 .
points = []
for x in numberOfColumns
points.push([])
for y in numberOfRows
points[x].push({x: x * widthOfColumn, y: y * heightOfRow})
참고 : 일반적으로 점 자체에 점 을 배치하지 않고 점 주위 에 격자를 만들 때 원점을 오프셋해야합니다 (열 너비의 절반을에서 행 높이의 절반을 뺀 값 ).x
y
이제 2D 배열 ( points
)을 초기화 했으므로 정사각형 그리드에서와 같이 마우스에서 가장 가까운 점을 찾을 수 있습니다. 두 번째 다이어그램에서 패턴을 생성하기 위해 다른 모든 점만 무시하면됩니다.
column, row = floor(mouse.x / columnWidth), floor(mouse.y / rowHeight)
point = null if column % 2 + row % 2 != 1 else points[column][row]
작동하지만 좌표가 포인터가 보이지 않는 사각형을 기준으로 가장 가까운 점 (또는 점 없음)으로 반올림됩니다. 점 주위에 원형 영역이 필요합니다 (따라서 스냅 범위는 모든 방향에서 동일합니다). 이제 어느 지점을 점검해야하는지 알았으므로 피타고라스 정리를 사용하여 거리를 쉽게 찾을 수 있습니다. 묵시적인 원은 여전히 원래 경계 사각형 안에 들어가야하며 최대 직경을 열 너비 (타일 너비의 1/4)로 제한해야하지만 실제로는 잘 작동하기에 충분히 큽니다.