Skip to content

Instantly share code, notes, and snippets.

@sknjpn
Created June 7, 2018 13:47
Show Gist options
  • Save sknjpn/6b2694e87c78818b2d68974e91036b00 to your computer and use it in GitHub Desktop.
Save sknjpn/6b2694e87c78818b2d68974e91036b00 to your computer and use it in GitHub Desktop.
# include <Siv3D.hpp> // OpenSiv3D v0.2.5
class Node
{
Vec2 m_position; //物理的な座標
Point m_point; //仮想上の座標
double m_height; //高度
double m_radius; //半径
Polygon m_polygon; //形状
Array<Node*> m_near_nodes;
public:
Node(Point point, Vec2 position, double r)
: m_point(point)
, m_position(position)
, m_height(0)
, m_radius(r)
, m_polygon(Shape2D::Hexagon(m_radius / 2.0 / Cos(30_deg), position).asPolygon())
{}
void add_near_node(Node* node) { m_near_nodes.emplace_back(node); }
//getter
Vec2 get_position() const { return m_position; }
double get_height() const { return m_height; }
Polygon get_polygon() const { return m_polygon; }
const Array<Node*>& get_near_nodes() const { return m_near_nodes; }
//setter
void set_height(double height) { m_height = height; }
};
class Field
{
double m_node_radius;
Point m_node_size;
Grid<Node*> m_nodes;
public:
Field(Vec2 size)
: m_node_radius(2.0)
, m_node_size(int(size.x / m_node_radius), int(size.y / Cos(30_deg) / m_node_radius))
, m_nodes(m_node_size)
{
init();
}
//マップ初期化
void init()
{
for (auto p : step(m_node_size))
{
//奇数列は半分ずらし、縦横比も変更
m_nodes[p.y][p.x] = new Node(p, m_node_radius * Vec2(p.x + (p.y % 2 ? Cos(60_deg) : 0.0), p.y * Cos(30_deg)), m_node_radius);
}
//Nodeの相互接続
for (auto p : step(m_node_size))
{
//周囲のリスト
Array<Point> list;
//奇数偶数列に応じた周囲のリストを登録
if (p.y % 2) { list = Array<Point>({ { p.x, p.y - 1 },{ p.x + 1, p.y - 1 },{ p.x + 1, p.y },{ p.x + 1, p.y + 1 },{ p.x, p.y + 1 },{ p.x - 1, p.y } }); }
else { list = Array<Point>({ { p.x - 1, p.y - 1 },{ p.x , p.y - 1 },{ p.x + 1, p.y },{ p.x , p.y + 1 },{ p.x - 1, p.y + 1 },{ p.x - 1, p.y } }); }
//周囲の結びつきの登録
for (auto& l : list)
{
if (l.x >= 0 && l.y >= 0 && l.x < m_node_size.x && l.y < m_node_size.y) { m_nodes[p.y][p.x]->add_near_node(m_nodes[l.y][l.x]); }
else { m_nodes[p.y][p.x]->add_near_node(nullptr); }
}
}
generate_terrain();
}
void generate_terrain()
{
PerlinNoise noise(Random(10000));
for (auto p : step(m_node_size))
{
auto* n = m_nodes[p.y][p.x];
auto h = Abs(noise.octaveNoise(n->get_position() * 0.0025, 10))
* Pow(1.0 - Abs(m_node_size.x / 2 - p.x) / double(m_node_size.x / 2), 0.5)
* Pow(1.0 - Abs(m_node_size.y / 2 - p.y) / double(m_node_size.y / 2), 0.5);
n->set_height(h);
}
}
void draw_hex()
{
//枠の描画
for (auto p : step(m_node_size))
{
auto* n = m_nodes[p.y][p.x];
n->get_polygon().draw(ColorF(Palette::Red, n->get_height()));
if (n->get_height() < 0.1)
{
n->get_polygon().draw(Palette::Blue);
}
//n->get_polygon().drawFrame(1.0, ColorF(1.0, 0.25));
}
/*
//接続線の描画
for (auto p : step(m_node_size))
{
auto* n1 = m_nodes[p.y][p.x];
for (auto* n2 : n1->get_near_nodes())
{
if (n2 != nullptr) { Line(n1->get_position(), n2->get_position()).draw(1.0, ColorF(1.0, 0.125)); }
}
}
*/
}
//等高線の描画
void draw_contour(double interval, double thickness)
{
for (double h = 0; h <= 1.0; h += interval)
{
for (auto p : step(m_node_size))
{
auto* tn = m_nodes[p.y][p.x];
const auto& nns = tn->get_near_nodes();
for (auto it = nns.begin(); it != nns.end(); it++)
{
auto* ln = *it; //left
auto* rn = (it == nns.end() - 1) ? *(nns.begin()) : *(it + 1); //right
if (ln == nullptr || rn == nullptr) { continue; }
if (h > tn->get_height() && h < ln->get_height() && h < rn->get_height())
{
auto c1 = tn->get_position().lerp(ln->get_position(), (h - tn->get_height()) / (ln->get_height() - tn->get_height()));
auto c2 = tn->get_position().lerp(rn->get_position(), (h - tn->get_height()) / (rn->get_height() - tn->get_height()));
Line(c1, c2).draw(thickness);
}
if (h < tn->get_height() && h > ln->get_height() && h > rn->get_height())
{
auto c1 = ln->get_position().lerp(tn->get_position(), (h - ln->get_height()) / (tn->get_height() - ln->get_height()));
auto c2 = rn->get_position().lerp(tn->get_position(), (h - rn->get_height()) / (tn->get_height() - rn->get_height()));
Line(c1, c2).draw(thickness);
}
}
}
}
}
void draw()
{
draw_hex();
draw_contour(0.1, 1.0);
draw_contour(0.025, 0.5);
}
};
Field* g_field = nullptr;
void Main()
{
g_field = new Field(Vec2(500, 500));
//ウィンドウの設定
Window::Resize(640, 480);
while (System::Update())
{
g_field->draw();
if (KeyEnter.down()) { g_field->generate_terrain(); }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment