Название: пока - "Прямой Лабиринт". Частично это в честь такого феномена, как неспособность не отклоняться от прямой, идя с закрытыми глазами (без ориентира, иначе говоря). Получается, что ровное тёмное поле без единой преграды способно конкурировать с лабиринтом из нагромождения препятствий. Феномен упоминался в "Разрушителях Легенд" (173 выпуск - 9 сезон, "пройтись по прямой"). Пространство: 3D Жанр: многопользовательская, браузерная Стилистика игры, графика: предположительно такая же, как сейчас в демо. Или другая, если будут удачные предложения. Кто и зачем нужен: в идеале: веб программист - писать алгоритм перемещения по полигонам, AI для ботов + системы клиент-клиент [p2p] и браузер-сервер; в не идеале (вне идеала): программист, но только для AI ботов и алгоритма перемещения по полигонам, никаких клиентов и серверов, только браузер или даже просто .exe Время на разработку: около недели; менее 16-ти часов* на код самой игры + часы для написания систем p2p и браузер-сервер. *Демо к игре было написано за 8 часов (не на движке), отсюда и придумал число 16. План, и зачем тут p2p: сначала будут тесты с одним игроком-человеком и AI противниками/союзниками; дальше, не зависимо от результата (win/fail), тесты с людьми, peer to peer, чтобы не платить за неизвестный результат; если результат - win, игра станет браузерной, с сайтом-сервером. Деньги будут изъяты из кармана программиста или выпрошены у третьей персоны. Моя роль в команде: геймдиз, конечно, да и вообще командир... Демо и пример исходника:https://www.dropbox.com/s/1mdxi1ez5pbwaj4/PolyDemo%20Zip.7z?dl=0 (2.02 Мб в распакованном виде) - демо программа (см. gif ниже), прилагаются исходники на Паскале. Кусок кода из исходника, содержащего алгоритм перемещения по полигонам [файл PolyMath.pas]:
Код
unit PolyMath;
{$modeswitch autoderef on}
interface
uses Math, SysUtils;
const Eps = 0.001;
type float = single;
function Interpolate(const a, b: float; const x: float): float; function Clamp(const x: float; const a, b: float): float; function FloatSign(const x: float): float;
type PVec3 = ^TVec3; TVec3 = object type TData = array[0 .. 2] of float; var data: TData; property X: float read data[0] write data[0]; property Y: float read data[1] write data[1]; property Z: float read data[2] write data[2]; function Make(const vx, vy, vz: float): TVec3; static; function SqrLength: float; function Length: float; function Normalized: TVec3; function MaybeNormalized: TVec3; // вектор по семантике нормализованный, но мог денормализоваться из-за ошибок округления function IsNormalized: boolean; function IsZero: boolean; function RandomDirection: TVec3; static; const Zero: TVec3 = (data: (0, 0, 0)); type TArray = array of TVec3; var function Split(const v: array of float): TArray; static; end; operator +(const a, b: TVec3): TVec3; operator -(const a, b: TVec3): TVec3; operator -(const v: TVec3): TVec3; operator *(const a, b: TVec3): TVec3; operator *(const k: float; const v: TVec3): TVec3; operator *(const v: TVec3; const k: float): TVec3; operator /(const v: TVec3; const k: float): TVec3; operator **(const a, b: TVec3): float; // скалярное произведение operator ><(const a, b: TVec3): TVec3; // векторное произведение function Interpolate(const a, b: TVec3; const pos: float): TVec3; function Distance(const a, b: TVec3): float; function Angle(const a, b: TVec3): float;
type TMat4 = object type TData = array[0 .. 3, 0 .. 3] of float; var data: TData; function ExplicitRows(const x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc, xd, xe, xf: float): TMat4; static; function FrustumProjection(const left, right, bottom, top, zNear, zFar: float): TMat4; static; function PerspectiveProjection(const yFov, aspect, zNear, zFar: float): TMat4; static; function Translation(const v: TVec3): TMat4; static; function Rotation(const angle: float; const axis: TVec3): TMat4; static; function Rotation(const a, b: TVec3): TMat4; static; function Determinant: float; function Inversed: TMat4;
// Частный случай — матрица поворота по направлениям вправо, вверх и назад. function WithBasis(const x, y, z: TVec3): TMat4; static; const Identity: TMat4 = (data: ((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))); end; operator *(const mat: TMat4; const vec: TVec3): TVec3; operator *(const a, b: TMat4): TMat4;
type TPlane = object type TData = record case integer of 0: (linear: array[0 .. 3] of float); 1: (v3: TVec3); end; var data: TData; property A: float read data.linear[0] write data.linear[0]; property B: float read data.linear[1] write data.linear[1]; property C: float read data.linear[2] write data.linear[2]; property D: float read data.linear[3] write data.linear[3]; property ABC: TVec3 read data.v3 write data.v3; function Make(const pa, pb, pc: TVec3): TPlane; static; function Make(const point, normal: TVec3): TPlane; static; function AnyPoint: TVec3; function IntersectLine(const p, v: TVec3; out int: TVec3): boolean; function IntersectSegment(const pa, pb: TVec3; out int: TVec3): boolean; end;
PFace = ^TFace; TFace = object v: array of integer; function Make(const newV: array of integer): TFace; static; function Validate(nv: integer): TFace; function CyclicV(i: integer): integer;
type TArray = array of TFace; function Split(vertsPerFace: integer; const vs: array of integer): TArray; static; end;
PPolyhedron = ^TPolyhedron; TPolyhedron = object vts: array of TVec3; faces: array of TFace;
// Вершины граней обходятся против часовой. function Make(const newVts: array of TVec3; const scale: float; const newFaces: array of TFace): TPolyhedron; static; function Make(const newVts: array of TVec3; const newFaces: array of TFace): TPolyhedron; static; function Vertex(face: PFace; id: integer): TVec3; function Normal(face: PFace): TVec3; function AdjacentFace(current: PFace; edge: integer): PFace; function EdgeVector(face: PFace; id: integer): TVec3; function MaxRadiusVector: float;
type TSingleFaceIntersection = record pos: TVec3; edge: integer; end; TFaceIntersection = array of TSingleFaceIntersection; function IntersectFace(face: PFace; const plane: TPlane): TFaceIntersection; function SameEdge(faceA: PFace; edgeA: integer; faceB: PFace; edgeB: integer): boolean; function Icosahedron(const radius: float): TPolyhedron; static; function Cube(const w, h, d: float): TPolyhedron; static; function TwoSidedHexagon(const radius: float): TPolyhedron; static; end;
implementation
function Interpolate(const a, b: float; const x: float): float; begin result := a + x * (b - a); end;
function Clamp(const x: float; const a, b: float): float; begin if x < a then result := a else if x > b then result := b else result := x; end;
function FloatSign(const x: float): float; begin if x > 0 then result := 1 else if x < 0 then result := -1 else result := 0; end;
function TVec3.Make(const vx, vy, vz: float): TVec3; begin result.x := vx; result.y := vy; result.z := vz; end;
function TVec3.SqrLength: float; begin result := sqr(x) + sqr(y) + sqr(z); end;
function TVec3.Length: float; begin result := sqrt(SqrLength); end;
function TVec3.Normalized: TVec3; var len: float; begin len := Length; if len = 0.0 then result := self else result := self / len; Assert(result.IsNormalized or result.IsZero); end;
function TVec3.MaybeNormalized: TVec3; begin if IsNormalized then result := self else result := Normalized; end;
function TVec3.IsNormalized: boolean; begin result := abs(SqrLength - 1) < Eps; end;
function TVec3.IsZero: boolean; begin result := (abs(x) < Eps) and (abs(y) < Eps) and (abs(z) < Eps); end;
function TVec3.RandomDirection: TVec3; var dz, len, angle: float; begin dz := 2 * random - 1; len := sqrt(1 - sqr(dz)); angle := random * (2 * pi); result := Make(cos(angle) * len, sin(angle) * len, dz); end;
function TVec3.Split(const v: array of float): TArray; var i, iv: integer; begin SetLength(result, System.length(v) div System.length(TData)); iv := 0; for i := 0 to High(result) do begin result[i] := TVec3.Make(v[iv], v[iv + 1], v[iv + 2]); iv += 3; end; end;
{$define impl := var i: integer; begin for i := 0 to High(result.data) do result.data[i] := {$ifdef un} un {$else} a.data[i] op b.data[i] {$endif}; end; {$undef op} {$undef un}} operator +(const a, b: TVec3): TVec3; {$define op := +} impl operator -(const a, b: TVec3): TVec3; {$define op := -} impl operator -(const v: TVec3): TVec3; {$define un := -v.data[i]} impl operator *(const a, b: TVec3): TVec3; {$define op := *} impl {$undef impl}
operator *(const k: float; const v: TVec3): TVec3; begin result := TVec3.Make(k * v.x, k * v.y, k * v.z); end;
operator *(const v: TVec3; const k: float): TVec3; begin result := k * v; end;
operator /(const v: TVec3; const k: float): TVec3; begin result := (1.0 / k) * v; end;
operator **(const a, b: TVec3): float; begin result := a.x * b.x + a.y * b.y + a.z * b.z; end;
function Interpolate(const a, b: TVec3; const pos: float): TVec3; begin result := a + pos * (b - a); end;
function Distance(const a, b: TVec3): float; begin result := (b - a).Length; end;
function Angle(const a, b: TVec3): float; var len: float; begin len := sqrt(a.SqrLength * b.SqrLength); if len < Eps then result := 0 else result := ArcCos((a ** b) / len); end; ...
Тред, откуда я взял (подарили) демо:
Описание и gif-иллюстрация из демо
Посмотрите на гифку. Вы видите персонажа, двигающегося, например, по кубу. Теперь представьте, что где-то на кубе есть ещё один такой персонаж. Мы не знаем, где он. А он нам опасен... но и мы опасны ему. Если вы представили такое, то поняли задумку.
Пояснение описания
Планета, карта
Представьте планету, но маленькую, уже и не планету, а астероид, если не меньше. Местность делят двое - соперники. Начав с полюсов, они движутся c целью поймать удачу, подскочить с тылу один к другому. Подскочил - умертвил. Выстрел как способ умерщвления. Вот в этом вся игра и всё соревнование.
Только вместо планеты, как бы сферы, в игре иной многогранник (присовокупляя и возможность двухстороннего многоугольника). Икосаэдр, куб, многоугольник - три нужных случая. Планета-икосаэдр, планета-куб, плоская планета.
Перемещение с полигона на полигон
Игрок переходит со стороны на сторону фигуры по прямой в развертке, она же кратчайший путь и геодезическая линия. Происходит поворот из нормали старого полигона в нормаль нового, углы 42°, 90° и 180° соответственно трём "нужным случаям": икосаэдру, кубу и многоугольнику.
Движение игрока и управление
Игрок подвержен дефолтному движению вперёд (по геодезической), движение неостановимо, но направляемо поворотами вправо и влево. Направляемо, или, точнее, поворачиваемо, в таком роде, что, если начнешь поворачиваться, будешь описывать определенную окружность. (простите, я пытался их сдерживать)
Инкорпорация
Задание для инкорпорации: выполнить наличествующее в демо за вычетом снежинок на фоне и крена. То есть исполнить перемещение с грани на грань, поворот из нормали одного полигона в нормаль другого. При этом не должно быть проблемой движение по ребру или переправа через вершину. Игрок идёт по прямой на развертке (кратчайший путь, см. wikipedia - Net (polyhedron) - Shortest Path). Обработать три случая: икосаэдр, куб, многоугольник. Управление - такое же, как в демо; игрок - шар, как и в демо. Выполнил - подставляй клеймоместо, ты принят. Последующие задания и тесты:
План тестов
Добавить изменение положения камеры, чтобы определять лучшее. Добавить ввод размера полигонов и скорости игрока, найти верное их соотношение, создать чувство достаточно быстрого движения. Добавить стрельбу и статичные мишени (на ребре, у ребра, на вершине, в центре полигона), разобраться с видимостью и длительностью жизни пули. Тесты игры с AI противником/ами/союзниками. Тесты игры с белковыми (потребуется онлайн).
Навигация
Цитата
Кто нужен для команды и какие требования к ним? Чем именно будешь заниматься сам?
"Кто и зачем нужен" + "Моя роль в команде"
Цитата
Как управлять героем?
"Движение игрока и управление"
Ответы
Цитата
В чем интерес игры?
Интерес вызывает осознание того, как мала, но при этом и достаточно велика, карта, как легко, но при этом и не очень, сойтись. А с кол-вом игроков, большим двух, замкнутая топология - просто хороший* способ генерировать беспорядок, тип которого - случайные встречи. Невозможно далеко уйти друг от друга. *Доказательство утверждения опущено.
Цитата
Не понятно будет ли интересно гонятся за противником, насколько удобно будет подобраться с тыла и насколько легко или сложно будет уклониться. В случае когда игрок не останавливается есть ли шанс его догнать?
Сложно назвать это "гонкой за противником", соперники сходятся случайно, можно только предсказать, где будет соперник. Уклониться либо невозможно, либо после того, как попал в соперника, произойдёт специальное событие, создающее тебе более выгодную ситуацию, коей нужно успеть воспользоваться.
Цитата
В случае когда игрок не останавливается есть ли шанс его догнать?
Игрок не знает, где другой игрок, поэтому не может его избежать.
Теоретически, конечно, соперники могут долго ездить по планете, не встречаясь, но практически... однако, если понадобится, проблему эту можно решить. Допустим, что иногда соперники получают информацию о местоположении друг друга*, или планета со временем будет уменьшаться, или скорость игроков будет увеличиваться, или ещё как-нибудь: до тестов рано выбирать. * Грубые способы: на мгновения показывать карту/ делать противника видимым через полигоны. Менее грубый способ: временно показывать направление, в какой стороне от игрока другой игрок. Интересный способ для куба, урезанная карта: показывать, как соперник перемещается на своем полигоне (но не указывать этот полигон) и противоположны ли направления игроков (для интерпретирования информации с урезанной карты).
Цитата
Будут ли активные препятсвтия(двигающиеся)? Какие и какое взаимодействие с ними? Будут ли враги помимо игроков?
На карте - только игроки, в игре - только игроки, никаких иных предметов... если не понадобятся (думаю нет).
Цитата
Самое главное это взаимодействие игроков, оно не отражено в демо. Вот эти моменты стоило показать в демо.
Мне не изменить - прототип не мой.
Добавлено (12 декабря 2014, 03:37) --------------------------------------------- Оп! Изменение темы, основное изменение - добавлен раздел "ответы". Топай как слон, обретешь уважение.
Сообщение отредактировал Pu14unkiihooiV - Четверг, 04 Июня 2015, 04:19
Я пониманию, в минуты сомнения сам себя об этом спрашиваю, но вопрос этот бессмысленен (по крайней мере мне так выгодно). Интерес вызывает осознание того, как мала, но при этом и достаточно велика, карта, как легко, но при этом и не очень, сойтись. А с кол-вом игроков, большим двух, замкнутая топология - просто хороший способ генерировать беспорядок, тип которого - случайные встречи. Невозможно далеко уйти друг от друга. Там потенциал, там зарыто, там чувство, там - осознание. ... Не убедил, да?
Добавил вопрос и ответ в "ответы". Топай как слон, обретешь уважение.
Сообщение отредактировал Pu14unkiihooiV - Пятница, 12 Декабря 2014, 17:18
Согласен с triptix. Слишком концептуальная идея. Действительно может бросить вызов программисту, и то не очень опытному, насколько я себе представляю. Но для игрока не будет интересна в силу множества причин: однообразность, отсутствие графики (либо примитивная), отсутствие системы поощрения игрока и т.п. В "готовых проектах" есть похожая концепция, но она была реализована за пару дней и не требовала такого объема работ при такой потенциально низкой отдаче. Текстовый контент для вашей игры (бесплатно) Сценарист, геймдизайнер для Вашей игры
Неа. Если только программисту будет интересно, чисто с точки зрения интересной задачки.
Демо, возможно, так и появилось - хождение по полигонам было задачей. Хорошо, если была бы ещё задача про AI. Хотя AI и есть задача, наверное.
Цитатаinventrix ()
triptix, хм, не знаю, по мне очень интересная задумка. Удачи.
Да, удача всегда пригодится.
ЦитатаAnthem ()
однообразность, отсутствие графики (либо примитивная), отсутствие системы поощрения игрока и т.п.
Есть такое, не отрицаю.
Цитата
В "готовых проектах" есть похожая концепция, но она была реализована за пару дней и не требовала такого объема работ при такой потенциально низкой отдаче.
Как найти эту тему? Топай как слон, обретешь уважение.