Оружие, простреливающее стены и игроков
Большинство из Вас наверняка играли в популярный мод к Half-Life под незамысловатым названием Counter Strike. Одной из приятнейших вещей этого мода является наличие оружия, достаточно сильных, чтобы пробивать стены. В случае с Half-Life дела обстоят довольно просто, т.к. в нем уже был ряд оружия, способного простреливать стены. Поэтому, все, что оставалось сделать рядовому модеру это незначительная настройка уже существующего оружия. Unreal Tournament, к сожалению, таким арсеналом похвастаться не может. Остается только своими силами добиться реализации подобного эффекта.
Без сомнения, существует много различных вариантов для "создания" такого вида оружия. Нижеописанный метод всего лишь один из многих пришедших в голову. У него есть свои недостатки (см. главу Недостатки), но в силу своей простоты и легкости реализации был выбран именно такой путь.
В Unreal Tournament оружия "мгновенного" типа (пистолет, пулемет, винтовка) используют понятие траектории (trace) для обнаружения конечной цели выстрела. Траектория начинается непосредственно с игрока, который выстрелил, затем происходит анализ вдоль траектории (прямой) до тех пор, пока линия не пересечется с объектом (стена, игрок и пр.). Если все это перенести графически на плоскость, то нормальное оружие стреляет таким образом:
Для того, чтобы заставить оружие прострелить стену, необходимо "запустить" новую траекторию на расстоянии Х от того места, где закончилась первая и в том же направлении. Х, в данном случае, представляет собой фактор проникновения, т.е. насколько толстую стену может пробить данное оружие. Если стена тоньше чем Х, то вторая траектория продолжиться с другой стороны, до тех пор пока она не пересечется с другим объектом:
Когда же стена толще, чем Х, исходная точка второй траектории будет находиться внутри стены и, достигнув внутренней части стены, остановится:
В реальном мире все намного сложнее. Проблема состоит в том, что игрок достаточно "толстый" и, соответственно, фактор проникновения должен быть достаточно высок, чтобы прострелить его. Если Вы установите для Х большое значение, то Ваше оружие будет простреливать стены намного толще, чем Вы бы сами того желали. Решением этой проблемы является назначение двух различных факторов проникновения: один для pawns (в число которых и входит игрок) и другой для всего остального. В конце первой траектории будет опрашиваться, с кем она пересеклась (pawn или же просто препятствие) и, в зависимости от ситуации, будет использован соответствующий фактор проникновения.
Звучит, как Вы видите, просто. Но как все это реализовать? И в этом также нет ничего сложного. Все, что для этого понадобиться, это перегрузить метод TraceFire() и в него заскриптить вызов второй траектории. Но, не думайте, что Вы можете скопировать и вставить этот код в любое оружие, и все будет ОК. Часть проблемы лежит в том, что перегруженный метод вызывает функцию ProcessTraceHit() дважды, что в свою очередь вызовет появления двух гильз и пр.
// Weapon fire that goes through wall // By Mr.Self-Destruct // Проникновение через стены var() float Thick; // Проникновение через pawns var() float PawnThick; function TraceFire( float Accuracy ) { local vector HitLocation, HitNormal, StartTrace, EndTrace, X,Y,Z; local actor Other; local Pawn PawnOwner; local float Penetration; PawnOwner = Pawn(Owner); Owner.MakeNoise(PawnOwner.SoundDampening); GetAxes(PawnOwner.ViewRotation,X,Y,Z); // Первая траектория, такая же, как и у обычного оружия StartTrace = Owner.Location + CalcDrawOffset() + FireOffset.X * X + FireOffset.Y * Y + FireOffset.Z * Z; AdjustedAim = PawnOwner.AdjustAim(1000000, StartTrace, 2*AimError, False, False); EndTrace = StartTrace + Accuracy * (FRand() - 0.5 )* Y * 1000 + Accuracy * (FRand() - 0.5 ) * Z * 1000; X = vector(AdjustedAim); EndTrace += (10000 * X); Other = PawnOwner.TraceShot(HitLocation,HitNormal,EndTrace,StartTrace); // Обработка контакта с первой целью ProcessTraceHit(Other, HitLocation, HitNormal, X,Y,Z); // Вторая траектория, ее исходная точка берется с учетом // конечной точки первой траектории if (Other.IsA('Pawn')) Penetration = PawnThick; else Penetration = Thick; StartTrace = HitLocation + HitNormal + (Penetration * X); EndTrace = StartTrace + Accuracy * (FRand() - 0.5 )* Y * 1000 + Accuracy * (FRand() - 0.5 ) * Z * 1000; EndTrace += (10000 * X); Other = PawnOwner.TraceShot(HitLocation,HitNormal,EndTrace,StartTrace); // Обработка контакта со второй целью ProcessTraceHit(Other, HitLocation, HitNormal, X,Y,Z); } defaultproperties { // Стены твердые, проникновение меньше Thick=20.0 // Pawns довольно мягкие и им нужно большее значение // проникновения чем стенам // Collision radius = 17 является значением по умолчанию в UT, поэтому // дадим фактору проникновения значение, превышающее толщину игрока PawnThick=36.0 } |
"И все!?" - удивитесь Вы, - "всего лишь 9 новых строчек!?" Но, как и было сказано в начале, все довольно просто.
Этот метод создания подобного оружия имеет свои недочеты, в основном видные уже на этапе теории:
Как и было сказано в начале, это не лучший способ решения проблемы для создания подобного оружия, но, тем не менее, этот метод достаточно прост и легок в реализации. Если кто-то хочет предложить другой способ (не очень сложный), то свяжитесь с автором и Ваша идея не останется в стороне.
Если Вы хотите посмотреть пример реальной реализации данного метода (кстати, в примере решена проблема появления двух гильз), загрузите мод Super Sniper Rifle, использующий вышеописанную технику с некоторыми доработками.
Mr.Self-Destruct
Источник: CHiMERiC
Перевод сделан 32_Pistoleta