Система стрельбы
Демонстрация
С помощью WASD передвигайте персонажа. С помощью ЛКМ производите срельбу. С помощью ПКМ прицеливаетесь. С помощью ножа режете
Мультиплеер
 +  Мультиплеерное
 +  Количество урона, скорости передвижения, скорости полёта снаряда, дистанция полёта снаряда и скорострельность настраиваются в редакторе объектов
 +  Динамическая камера
 +  Учитывается высота противников для стрельбы, на которых направлена мышка
 +  Отсутствие движения мышки учитывается при ходьбе
 +  Прицеливание уменьшает разброс пуль
 -  Не изменяется положение туловища и ног по Pitch'y
Предыстория
Я редко встречаю карты с системами стрельбы через мышку и на WASD'e, тем более мультиплеерные и тем более на ванилле (я и не ищу их, я ищу счастье, гармонию от слияния с бесконечно вечным)
Референс для этой наработки взят из Морозко против Зла, где подобное было выполнено горекриво на мемхаке и с отсутствием мультиплеера. Систему можно самостоятельно очень сильно развить, добавив на тот же пробел кувырок/рывок/телепорт/прыжок/джетпак персонажа, на ПКМ атаку ближнего боя, на Q/E смену оружия, которое можно самостоятельно сделать абсолютно различными вариациями, ну и так далее. Я не делал ориентацию туловища/ног для этой наработки, поскольку есть 2 варианта это сделать:
- SetUnitLookAt - старый, рабочий костыль, требующий даммика в позиции курсора.
- UnitEnableAutoOrientation + SetUnitOrientation - нативки UjAPI, требует сглаживания. Варкрафтовское изменение ориентации работать больше не будет, GetUnitFacing нужно заменять на GetUnitYaw.
Инструкция по импорту
- Работает исключительно на UjAPI, поэтому вам необходимо установить лаунчер
Скопировать папку Initialization и закинуть в свою карту вместе со всем импортом
Триггер Sound нужен для позиционированных звуков стрельбы и взят с уже существующей наработки 3д звук
В триггере ShooterSystem указать на 324/325 строках равкод туловища/ног персонажа, на строке 258 указать радиус пули, на строках 247-253 указать разброс пуль при свободной стрельбе и прицеливании, на строках 178, 179 и 214 указать во втором аргументе функции индекс анимации ходьбы. Всё остальное требует минимальных знаний джасса
если что-то не понятно - комменты
Код
У меня нет настроения расписывать какое это говно, так что просто представьте, что не представьте, первый раз космос погибнет в уме - второй раз в сердце, мир это лишь твой проектор вне, спасибо за непонимание
AllGlobals
library AllGlobalsLib initializer OnInit
globals
    constant hashtable H = InitHashtable( )
    
    unit array PlayerUnitUnderCursor
    real array PlayerMouseWorldX
    real array PlayerMouseWorldY
    real array PlayerMouseWorldZ
    real array PlayerMouseWorldXLast
    real array PlayerMouseWorldYLast
    
    real MaxX
    real MinX
    real MaxY
    real MinY
endglobals
function AngleDifference takes real a, real a1 returns real
	set a = RAbsBJ( a - a1 )
	
	if a > 180.00 then
		set a = 360.00 - a
	endif
	return a
endfunction
function SetUnitPositionSmooth takes unit source, real x, real y returns nothing
    local real last_x = GetUnitX(source)
    local real last_y = GetUnitY(source)
    local boolean bx
    local boolean by
    call SetUnitPosition(source, x, y)
    if (RAbsBJ(GetUnitX(source) - x) > 0.5) or (RAbsBJ(GetUnitY(source) - y) > 0.5) then
        
        call SetUnitPosition(source, x, last_y)
        set bx = RAbsBJ(GetUnitX(source) - x) <= 0.5
        call SetUnitPosition(source, last_x, y)
        set by = RAbsBJ(GetUnitY(source) - y) <= 0.5
        
        if bx then
            call SetUnitPosition(source, x, last_y)
        elseif by then
            call SetUnitPosition(source, last_x, y)
        else
            call SetUnitPosition(source, last_x, last_y)
        endif
    endif
endfunction
function SetUnitPositionEX takes unit u, real x, real y returns nothing
    if x > MaxX then
        set x = MaxX
    elseif x < MinX then
        set x = MinX
    endif
    
    if y > MaxY then
        set y = MaxY
    elseif y < MinY then
        set y = MinY
    endif
    
    call SetUnitX( u, x )
    call SetUnitY( u, y )
endfunction
function CreateUnitEx takes player id, integer unitid, real x, real y, real face returns unit
    set bj_lastCreatedUnit = CreateUnit( id, unitid, x, y, face )
    
    call SetUnitPositionEX( bj_lastCreatedUnit, x, y )
    
    return bj_lastCreatedUnit
endfunction
function TrackUnitUnderCursor takes nothing returns nothing
    set PlayerUnitUnderCursor[ GetPlayerId( GetTriggerPlayer( ) ) ] = GetTriggerUnit( )
endfunction
function TrackMouseWorldXYZ takes nothing returns nothing
    local integer i = GetPlayerId( GetTriggerPlayer( ) )
    
    set PlayerMouseWorldX[i] = GetTriggerPlayerMouseWorldX( )
    set PlayerMouseWorldY[i] = GetTriggerPlayerMouseWorldY( )
    set PlayerMouseWorldZ[i] = GetTriggerPlayerMouseWorldZ( )
endfunction
private function OnInit takes nothing returns nothing
    local trigger trg = CreateTrigger( )
    local trigger ttg = CreateTrigger( )
    local integer i = 0
    local rect r = GetWorldBounds( )
    
    call SetMouseMoveEventWorldAxisEnabled( true )
    call SetMouseMoveEventScreenAxisEnabled( false )
    
    loop
        call TriggerRegisterPlayerEvent( trg, Player( i ), EVENT_PLAYER_MOUSE_MOVE )
        call TriggerRegisterPlayerEvent( ttg, Player( i ), EVENT_PLAYER_WIDGET_TRACK )
        
        set i = i + 1
        exitwhen i >= bj_MAX_PLAYER_SLOTS
    endloop
    
    call TriggerAddAction( trg, function TrackMouseWorldXYZ )
    call TriggerAddAction( ttg, function TrackUnitUnderCursor )
    
    set MaxX = GetRectMaxX( r )
    set MinX = GetRectMinX( r )
    set MaxY = GetRectMaxY( r )
    set MinY = GetRectMinY( r )
    
    call RemoveRect( r )
    
    set r = null
    set trg = null
    set ttg = null
endfunction
endlibrary
//===========================================================================
//function InitTrig_AllGlobals takes nothing returns nothing
    //set gg_trg_AllGlobals = CreateTrigger(  )
//endfunction
ShooterSystem
library ShooterSystem initializer OnInit requires SoundLib
globals
    private constant timer CameraMotionTimer = CreateTimer( )
    private constant real TimerPeriodic = 0.02125
    private constant group TempGroup = CreateGroup( )
    
    private timer array TempTimer
    
    private boolean array BUTTON_PRESSED[195][12]
    private boolean array IsMoved
    
    private string array BulletModel
    
    private real array ReloadTime
    
    unit array CharacterBody
    unit array CharacterLegs
endglobals
private struct vector
    real x
    real y
    real z
    
    method length takes nothing returns real
        return SquareRoot( x * x + y * y + z * z )
    endmethod
    
    method normalize takes nothing returns nothing
        local real l = length( )
        
        if l == 0.00 then
            set l = 1.00
        endif
        
        set x = x / l
        set y = y / l
        set z = z / l
    endmethod
    
    static method create takes real x, real y, real z returns thistype
        local thistype this = thistype.allocate( )
        
        set this.x = x
        set this.y = y
        set this.z = z
        
        return this
    endmethod
endstruct
private struct bulletS
    vector v
    vector p
    
    real speed
    real damage
    real radius
    real distance
    effect bullet
    player pl
    timer t
    
    method Destroy takes nothing returns nothing
        call PauseTimer( t )
        call FlushChildHashtable( H, GetHandleId( t ) )
        call DestroyTimer( t )
            
        call DestroyEffect( bullet )
        
        set t = null
        set bullet = null
        
        call p.destroy( )
        call v.destroy( )
        call destroy( )
    endmethod
    
    static method move takes nothing returns nothing
        local thistype this = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
        local unit u
        
        if this.distance < this.speed then
            set this.speed = this.distance
        endif
        
        set this.p.x = this.p.x + this.speed * this.v.x
        set this.p.y = this.p.y + this.speed * this.v.y
        set this.p.z = this.p.z + this.speed * this.v.z
        set this.distance = this.distance - this.speed
        
        call SetSpecialEffectPositionWithZ( this.bullet, this.p.x, this.p.y, this.p.z )
        
        call GroupEnumUnitsInRange( TempGroup, this.p.x, this.p.y, this.radius + 200.00, null )
        
        loop
            set u = FirstOfGroup( TempGroup )
            exitwhen u == null
            call GroupRemoveUnit( TempGroup, u )
            
            if IsUnitInRangeXY( u, this.p.x, this.p.y, this.radius ) then
                if IsUnitAlive( u ) and IsUnitEnemy( u, this.pl ) then
                    if RAbsBJ( GetUnitZ( u ) - this.p.z ) <= this.radius + GetUnitRealField( u, UNIT_RF_COLLISION_SIZE ) * 2.00 then
                        call UnitDamageTarget( CharacterBody[ GetPlayerId( this.pl ) ], u, this.damage, false, false, null, null, null )
                        
                        call GroupClear( TempGroup )
                        set this.distance = 0.00
                    endif
                endif
            endif
        endloop
        
        if this.distance <= 0.00 or this.p.z <= GetAxisZ( this.p.x, this.p.y ) then
            call this.Destroy( )
        endif
    endmethod
    
    method launch takes nothing returns nothing
        set t = CreateTimer( )
        
        call SaveInteger( H, GetHandleId( t ), 0, this )
        call TimerStart( t, TimerPeriodic, true, function thistype.move )
    endmethod
endstruct
private function CheckMoves takes nothing returns nothing
    local integer i = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local integer j = 0
    local real a = 0.00
    local real s = 0.00
    local real k
    local real d
    local bulletS A
    
    if BUTTON_PRESSED[ GetHandleId( OSKEY_W ) ][i] then
        set a = a + 90.00
        set j = j + 1
    elseif BUTTON_PRESSED[ GetHandleId( OSKEY_S ) ][i] then
        set a = a + 270.00
        set j = j + 1
    endif
    
    if BUTTON_PRESSED[ GetHandleId( OSKEY_A ) ][i] then
        set a = a + 180.00
        set j = j + 1
    elseif BUTTON_PRESSED[ GetHandleId( OSKEY_D ) ][i] then
        set a = a + 360.00
        if a == 450.00 then
            set a = a - 360.00
        endif
        set j = j + 1
    endif
    
    if j != 0 then
        set a = a / j
        set s = GetUnitMoveSpeed( CharacterBody[i] ) * 0.01
        set d = GetUnitFacing( CharacterBody[i] )
        set k = AngleDifference( a, d )
        
        if k >= 95.00 then
            if k >= 135.00 then
                set s = s * 0.85
            else
                set s = s * 0.75
            endif
            
            call SetUnitTimeScale( CharacterBody[i], -1.00 )
            call SetUnitTimeScale( CharacterLegs[i], -1.00 )
            call SetUnitFacingInstant( CharacterLegs[i], d )
        else
            call SetUnitTimeScale( CharacterBody[i], 1.00 )
            call SetUnitTimeScale( CharacterLegs[i], 1.00 )
            call SetUnitFacing( CharacterLegs[i], a )
        endif 
        
        call SetUnitPositionSmooth( CharacterBody[i], GetUnitX( CharacterBody[i] ) + s * MathCosDeg( a ), GetUnitY( CharacterBody[i] ) + s * MathSinDeg( a ) )
        call SetUnitX( CharacterLegs[i], GetUnitX( CharacterBody[i] ) )
        call SetUnitY( CharacterLegs[i], GetUnitY( CharacterBody[i] ) )
        
        if not IsMoved[i] then
            set IsMoved[i] = true
            call SetUnitAnimationByIndex( CharacterBody[i], 7 )
            call SetUnitAnimationByIndex( CharacterLegs[i], 7 )
        endif
    else
        call SetUnitFacing( CharacterLegs[i], GetUnitFacing( CharacterBody[i] ) )
        
        if IsMoved[i] then
            set IsMoved[i] = false
            
            call SetUnitTimeScale( CharacterBody[i], 1.00 )
            call SetUnitTimeScale( CharacterLegs[i], 1.00 )
            call SetUnitAnimation( CharacterBody[i], "stand" )
            call SetUnitAnimation( CharacterLegs[i], "stand" )
        endif
    endif
    
    if PlayerMouseWorldXLast[i] == PlayerMouseWorldX[i] then
        set PlayerMouseWorldX[i] = PlayerMouseWorldX[i] + s * MathCosDeg( a )
    endif
    
    if PlayerMouseWorldYLast[i] == PlayerMouseWorldY[i] then
        set PlayerMouseWorldY[i] = PlayerMouseWorldY[i] + s * MathSinDeg( a )
    endif
    
    set PlayerMouseWorldXLast[i] = PlayerMouseWorldX[i]
    set PlayerMouseWorldYLast[i] = PlayerMouseWorldY[i]
    
    if ReloadTime[i] > 0.00 then
        set ReloadTime[i] = ReloadTime[i] - 0.01
    elseif BUTTON_PRESSED[ GetHandleId( OSKEY_LBUTTON ) ][i] then
        set ReloadTime[i] = GetUnitWeaponRealField( CharacterBody[i], UNIT_WEAPON_RF_ATTACK_BASE_COOLDOWN, 0 )
        
        call SetUnitAnimation( CharacterBody[i], "attack" )
        call QueueUnitAnimation( CharacterBody[i], "ready" )
        
        if IsMoved[i] then
            call QueueWidgetAnimationByIndex( CharacterBody[i], 7 )
        else
            call QueueUnitAnimation( CharacterBody[i], "stand" )
        endif
        
        set A = bulletS.create( )
        
        set A.p = vector.create( GetUnitX( CharacterBody[i] ), GetUnitY( CharacterBody[i] ), GetUnitZ( CharacterBody[i] ) + 60.00 )
        
        if PlayerUnitUnderCursor[i] != null and PlayerUnitUnderCursor[i] != CharacterBody[i] then
            if GetUnitZ( PlayerUnitUnderCursor[i] ) - 60.00 > GetUnitZ( CharacterBody[i] ) then
                set A.v = vector.create( GetUnitX( PlayerUnitUnderCursor[i] ) - A.p.x, GetUnitY( PlayerUnitUnderCursor[i] ) - A.p.y, GetUnitZ( PlayerUnitUnderCursor[i] ) - A.p.z )
            else
                set A.v = vector.create( GetUnitX( PlayerUnitUnderCursor[i] ) - A.p.x, GetUnitY( PlayerUnitUnderCursor[i] ) - A.p.y, GetUnitZ( PlayerUnitUnderCursor[i] ) + 60.00 - A.p.z )
            endif
        else
            set A.v = vector.create( PlayerMouseWorldX[i] - A.p.x, PlayerMouseWorldY[i] - A.p.y, PlayerMouseWorldZ[i] + 60.00 - A.p.z )
        endif
        
        set A.pl = Player( i )
        set A.bullet = AddSpecialEffect( BulletModel[ GetRandomInt( 0, 5 ) ], A.p.x, A.p.y )
        
        call SetSpecialEffectZ( A.bullet, A.p.z )
        call SetSpecialEffectOrientation( A.bullet, Atan2( A.v.y, A.v.x ) * bj_RADTODEG, Atan2( A.v.z, A.v.length( ) ) * bj_RADTODEG, 0.00 )
        
        set bj_lastCreatedEffect = AddSpecialEffect( "confetti_up_small.mdx", A.p.x, A.p.y )
        call SetSpecialEffectZ( bj_lastCreatedEffect, A.p.z )
        call SetSpecialEffectOrientation( bj_lastCreatedEffect, GetSpecialEffectYaw( A.bullet ), GetSpecialEffectPitch( A.bullet ) - 80.00, 0.00 )
        call DestroyEffect( bj_lastCreatedEffect )
        
        call A.v.normalize( )
        
        if BUTTON_PRESSED[ GetHandleId( OSKEY_RBUTTON ) ][i] then
            set A.v.x = A.v.x + GetRandomReal( -0.04, 0.04 )
            set A.v.y = A.v.y + GetRandomReal( -0.04, 0.04 )
            set A.v.z = A.v.z + GetRandomReal( -0.01, 0.01 )
        else
            set A.v.x = A.v.x + GetRandomReal( -0.08, 0.08 )
            set A.v.y = A.v.y + GetRandomReal( -0.08, 0.08 )
            set A.v.z = A.v.z + GetRandomReal( -0.03, 0.03 )
        endif
        
        set A.speed = GetUnitWeaponRealField( CharacterBody[i], UNIT_WEAPON_RF_ATTACK_PROJECTILE_SPEED, 0 ) * TimerPeriodic
        set A.damage = GetRandomInt( GetUnitWeaponIntegerField( CharacterBody[i], UNIT_WEAPON_IF_ATTACK_DAMAGE_BASE_MINIMUM, 0 ), GetUnitWeaponIntegerField( CharacterBody[i], UNIT_WEAPON_IF_ATTACK_DAMAGE_BASE_MAXIMUM, 0 ) ) + GetUnitWeaponIntegerField( CharacterBody[i], UNIT_WEAPON_IF_ATTACK_DAMAGE_BONUS, 0 )
        set A.radius = 32.00
        set A.distance = GetUnitWeaponRealField( CharacterBody[i], UNIT_WEAPON_RF_ATTACK_RANGE, 0 )
        
        call Sound.playPoint( "groza_shoot" + I2S( GetRandomInt( 0, 1 ) ) + ".wav", A.p.x, A.p.y, 600.00, 3000.00, GetRandomInt( 60, 80 ) )
        call Sound.playPoint( "sleigh bells" + I2S( GetRandomInt( 0, 3 ) ) + ".wav", A.p.x, A.p.y, 600.00, 3000.00, GetRandomInt( 60, 80 ) )
        
        call A.launch( )
    endif
    
    call SetUnitFacing( CharacterBody[i], MathAngleBetweenPoints( GetUnitX( CharacterBody[i] ), GetUnitY( CharacterBody[i] ), PlayerMouseWorldX[i], PlayerMouseWorldY[i] ) )
endfunction
private function CharacterMove takes nothing returns nothing
    set BUTTON_PRESSED[ GetHandleId( GetTriggerPlayerKey( ) ) ][ GetPlayerId( GetTriggerPlayer( ) ) ] = GetTriggerPlayerIsKeyDown( )
endfunction
private function SetCameraMotion takes nothing returns nothing
    local integer i = GetPlayerId( GetLocalPlayer( ) )
    local real wh = GetWindowHeight( )
    local real ww = GetWindowWidth( )
    local real x  = ww * ( GetMouseScreenX( ) / 0.80 )
    local real y  = wh * ( GetMouseScreenY( ) / 0.60 )
    local real a  = Atan2( y - wh / 2.00, x - ww / 2.00 )
    local real d 
    
    if BUTTON_PRESSED[ GetHandleId( OSKEY_RBUTTON ) ][i] then
        set d = SquareRoot( ( x - ww / 2.00 ) * ( x - ww / 2.00 ) + ( y - wh / 2.00 ) * ( y - wh / 2.00 ) ) * 1.50
        
        call SetCameraField( CAMERA_FIELD_TARGET_DISTANCE, 1850.00 + ( GetAxisZ( x, y ) + GetUnitFlyHeight( CharacterBody[i] ) ) * 0.50, 0.10 )
    else
        set d = RMinBJ( SquareRoot( ( x - ww / 2.00 ) * ( x - ww / 2.00 ) + ( y - wh / 2.00 ) * ( y - wh / 2.00 ) ) * 0.75, 100.00 + 300.00 * ww / 1920.00 )
        
        call SetCameraField( CAMERA_FIELD_TARGET_DISTANCE, 2000.00 + ( GetAxisZ( x, y ) + GetUnitFlyHeight( CharacterBody[i] ) ) * 0.50, 0.10 )
    endif
    
    set x = GetUnitX( CharacterBody[i] )
    set y = GetUnitY( CharacterBody[i] )
    call PanCameraToTimed( x + d * Cos( a ), y + d * Sin( a ), 0.01 )
endfunction
private function OnInit takes nothing returns nothing
    local trigger trgWASD = CreateTrigger( )
    local integer i = 0
    local player p
    
    loop
        set p = Player( i )
        
        if GetPlayerController( p ) == MAP_CONTROL_USER and GetPlayerSlotState( p ) == PLAYER_SLOT_STATE_PLAYING then
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_W, META_KEY_NONE, true )
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_A, META_KEY_NONE, true )
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_S, META_KEY_NONE, true )
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_D, META_KEY_NONE, true )
            
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_W, META_KEY_NONE, false )
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_A, META_KEY_NONE, false )
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_S, META_KEY_NONE, false )
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_D, META_KEY_NONE, false )
            
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_LBUTTON, META_KEY_NONE, true )
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_RBUTTON, META_KEY_NONE, true )
            
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_LBUTTON, META_KEY_NONE, false )
            call TriggerRegisterPlayerKeyEvent( trgWASD, p, OSKEY_RBUTTON, META_KEY_NONE, false )
            
            // створення персонажу
            set CharacterBody[i] = CreateUnitEx( p, 'h000', 0.00, 0.00, 0.00 )
            set CharacterLegs[i] = CreateUnitEx( p, 'h001', 0.00, 0.00, 0.00 )
            
            call AddSpecialEffectTarget( "autogun.mdx", CharacterBody[i], "hand right ref" )
            call SetUnitPathing( CharacterLegs[i], false )
            
            set TempTimer[i] = CreateTimer( )
            
            call SaveInteger( H, GetHandleId( TempTimer[i] ), 0, i )
            call TimerStart( TempTimer[i], 0.01, true, function CheckMoves )
            
            set IsMoved[i] = false
        endif
        
        set i = i + 1
        exitwhen i >= bj_MAX_PLAYER_SLOTS
    endloop
    
    call TriggerAddAction( trgWASD, function CharacterMove )
    call TimerStart( CameraMotionTimer, 0.01, true, function SetCameraMotion )
    
    call EnableSelect( false, false )
    call EnablePreSelect( false, false )
    call EnableDragSelect( false, false )
    
    set BulletModel[0] = "Shot Blue.mdx"
    set BulletModel[1] = "Shot Green.mdx"
    set BulletModel[2] = "Shot Orange.mdx"
    set BulletModel[3] = "Shot Purple.mdx"
    set BulletModel[4] = "Shot Red.mdx"
    set BulletModel[5] = "Shot Yellow.mdx"
    
    set trgWASD = null
endfunction
endlibrary
//===========================================================================
//function InitTrig_ShooterSystem takes nothing returns nothing
    //set gg_trg_ShooterSystem = CreateTrigger(  )
//endfunction
 
                        
                        
                    
 WC3
                                WC3
                            




 
                             
                             
                             
                             
                            
выстрелить себе в голоя обязательно не исправлюсь