• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Cam System v3

Updating the description. Give it a second.

Code for the people who're too lazy to test the map.

Camera System
JASS:
//! zinc

    library CameraSystem requires optional CameraKeyboardMovement
    {
        //////////////////////////////////////////////////////////////////////////
        //
        //      Configuration area
        
        //          - Just default variables which the system will use when no other ones are specified by the user.
        constant    real    CAMERA_DEFAULT_X        =   0,
                            CAMERA_DEFAULT_Y        =   0,
                            CAMERA_DEFAULT_ZANGLE   =   350,
                            CAMERA_DEFAULT_ZHEIGHT  =   190,
                            CAMERA_DEFAULT_ROTATION =   0,
                            CAMERA_DEFAULT_DISTANCE =   175,
                            
        //          - This is tricky. It defines the weight the measurements of the z position have. This system measures
        //            out 4 locations to get a neat z height, 2 of them are at the unit's location and 2 are at the camera
        //            location which is a bit further away. Those values define which measure has more weight - if you have
        //            low terrain with less cliff heights you should probably give the unit's measurement more weight.
        //            If you don't understand a bit of what I'm trying to explain, just play around a bit with those values
        //            and look what effect they might have.
                            CAMERA_TARGET_WEIGHT    =   1,
                            CAMERA_CAMERA_WEIGHT    =   2 - CAMERA_TARGET_WEIGHT,
        //          - What difference the measures have, if you have really steep cliffs (I'm not talking about blizzard cliffs)
        //            you should probably decrease this.
                            CAMERA_ZDIST_MEASURE    =   50,
                            
        //          - Pretty much self-explaining. You don't really have to change anything here.
                            CAMERA_LOOP_INTERVAL    =   0.01,
                            CAMERA_UPDATE_INTERVAL  =   0.2;
        //////////////////////////////////////////////////////////////////////////
        //
        //      Struct area
        
        public struct CAMERA
        {
        
        //////////////////////////////////////////////////////////////////////////
        //
        //      Struct variable area
        
            public
            {
                real x = CAMERA_DEFAULT_X, y = CAMERA_DEFAULT_Y,
                     zAngle = CAMERA_DEFAULT_ZANGLE, zHeight = CAMERA_DEFAULT_ZHEIGHT,
                     rotation = CAMERA_DEFAULT_ROTATION, distanceToTarget = CAMERA_DEFAULT_DISTANCE;
                
                player applyFor = null;
                unit   toFollow = null;
            }
            
            private
            {
                integer node;
                boolean activated = false;
            
                static timer t = CreateTimer();
                static integer count = 0;
                static thistype data[];
                
                static location loc = Location(0,0);
            }
            
            optional module CameraKeyboardMovement;
            
        //////////////////////////////////////////////////////////////////////////
        //
        //      Struct method area
            
            static method create(player p) -> thistype
            {
                thistype this = thistype.allocate();
                applyFor = p;
                static if (thistype.movementInit.exists) { movementInit(); }
                return this;
            }
            
            method apply()
            {
                if (count == 0)
                    TimerStart(t, CAMERA_LOOP_INTERVAL, true, static method thistype.cameraLoop);
                    
                data[count] = this;
                node = count;
                count += 1;
                
                activated = true;
            }
            
            private static method cameraLoop()
            {
                real tX = 0, tY = 0, tZ[], tzAngle = 0, tzHeight = 0, tRotation = 0;
                thistype this;
                integer i = 0;
                
                while (i < count)
                {
                    this = data[i];
                    static if (thistype.movement.exists) { movement(); }
                    
                    if (toFollow != null)
                    {
                        x = GetUnitX(toFollow); y = GetUnitY(toFollow);
                        tRotation = GetUnitFacing(toFollow)*bj_DEGTORAD;
                        tzHeight = GetUnitFlyHeight(toFollow);
                    }
                    
                    tX = x; tY = y;
                    
                    tX += CAMERA_ZDIST_MEASURE/2 * Cos(tRotation);
                    tY += CAMERA_ZDIST_MEASURE/2 * Sin(tRotation);
                    MoveLocation(loc, tX, tY);
                    tZ[0] = GetLocationZ(loc);
                    
                    tX -= CAMERA_ZDIST_MEASURE * Cos(tRotation);
                    tY -= CAMERA_ZDIST_MEASURE * Sin(tRotation);
                    MoveLocation(loc, tX, tY);
                    tZ[1] = GetLocationZ(loc);
                    
                    tX = GetCameraTargetPositionX() + CAMERA_ZDIST_MEASURE/2 * Cos(tRotation);
                    tY = GetCameraTargetPositionY() + CAMERA_ZDIST_MEASURE/2 * Sin(tRotation);
                    MoveLocation(loc, tX, tY);
                    tZ[2] = GetLocationZ(loc);
                    
                    tX -= CAMERA_ZDIST_MEASURE * Cos(tRotation);
                    tY -= CAMERA_ZDIST_MEASURE * Sin(tRotation);
                    MoveLocation(loc, tX, tY);
                    tZ[3] = GetLocationZ(loc);
                    
                    tZ[2] = ((tZ[0]-tZ[1])*CAMERA_TARGET_WEIGHT+(tZ[2]-tZ[3])*CAMERA_CAMERA_WEIGHT)/4;
                    
                    tX = x; tY = y;
                    
                    tRotation += rotation*bj_DEGTORAD;
                    tX -= (distanceToTarget+tZ[2]) * Cos(tRotation);
                    tY -= (distanceToTarget+tZ[2]) * Sin(tRotation);
                    MoveLocation(loc, tX, tY);
                    
                    tzAngle = zAngle + tZ[2];
                    tzHeight += zHeight + GetCameraField(CAMERA_FIELD_ZOFFSET) + GetLocationZ(loc) + RAbsBJ(tZ[2]) - GetCameraEyePositionZ();
                    
                    if (GetLocalPlayer() == applyFor)
                    {
                        PanCameraToTimed(tX, tY, CAMERA_UPDATE_INTERVAL);
                        SetCameraField(CAMERA_FIELD_ZOFFSET, tzHeight, CAMERA_UPDATE_INTERVAL);
                        SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, tzAngle, CAMERA_UPDATE_INTERVAL);
                        SetCameraField(CAMERA_FIELD_ROTATION, tRotation*bj_RADTODEG, CAMERA_UPDATE_INTERVAL);
                        SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, distanceToTarget+tZ[2], CAMERA_UPDATE_INTERVAL);
                    }
                    
                    i += 1;
                }
            }
            
            method operator active() -> boolean
                { return activated; }
            
            method operator active=(boolean b)
            {
                if (b && !activated)
                    apply();
                else if (!b && activated)
                {
                    data[node] = data[count-1];
                    data[node].node = node;
                    count -= 1;
                    
                    if (count == 0)
                        PauseTimer(t);
                        
                    activated = false;
                }
            }
            
            method destroy()
            {
                if (activated)
                {
                    data[node] = data[count-1];
                    data[node].node = node;
                    count -= 1;
                    
                    if (count == 0)
                        PauseTimer(t);
                }
                
                deallocate();
            }
        }
    }

//! endzinc

Keyboard movement
JASS:
//! zinc

    library CameraKeyboardMovement
    {
    
        //CONFIGURABLES
            //How much a unit should turn around when pressing left/right. Leave the * bj_DEGTORAD, or it won't work.
            //Default is the unit's turn speed, if you want to leave that, just set TURN_ANGLE to 0.
            constant real    TURN_ANGLE      = 0 * bj_DEGTORAD;
            //Animation that the unit get's when no key is pressed.
            constant string  ANIM_STOP       = "stand";
            //Default animation of a unit if none is found.
            constant integer ANIM_DEFAULT    = 6;
            
            //Same for this library, because the cam variables are private. (And should stay so)
            constant real    LOOP_INTERVAL   = 0.01,
                             UPDATE_INTERVAL = 0.2;
            
            function CanUnitMove(unit who) -> boolean
            {
                return UnitAlive(who) && !IsUnitPaused(who) && !(GetUnitAbilityLevel(who, 'Bens') > 0) && /*
                  */!(GetUnitAbilityLevel(who, 'Bena') > 0) && !(GetUnitAbilityLevel(who, 'Beng') > 0)     && /*
                  */!(GetUnitAbilityLevel(who, 'BSTN') > 0) && !(GetUnitAbilityLevel(who, 'BPSE') > 0);
            }
        //CONFIGURABLES
    
        CAMERA TriggerCam = -1;
        public CAMERA PlayerCameras[];
        
        public function GetTriggerCam() -> CAMERA
        {
            return TriggerCam;
        }
    
        public module CameraKeyboardMovement
        {
            integer animationIndex = ANIM_DEFAULT;
            real animationDur = 0;
        
            private
            {
                boolean upPressed, downPressed, rightPressed, leftPressed, playAnimation;
                trigger onKeyEvent;
                
                real animationCount = 0;
            }
        
            method movementInit()
            {
                upPressed = false; downPressed = false; rightPressed = false; leftPressed = false; playAnimation = false;
                
                onKeyEvent = CreateTrigger();
                TriggerRegisterPlayerEvent(onKeyEvent, applyFor, EVENT_PLAYER_ARROW_UP_DOWN);
                TriggerRegisterPlayerEvent(onKeyEvent, applyFor, EVENT_PLAYER_ARROW_UP_UP);
                TriggerRegisterPlayerEvent(onKeyEvent, applyFor, EVENT_PLAYER_ARROW_DOWN_DOWN);
                TriggerRegisterPlayerEvent(onKeyEvent, applyFor, EVENT_PLAYER_ARROW_DOWN_UP);
                TriggerRegisterPlayerEvent(onKeyEvent, applyFor, EVENT_PLAYER_ARROW_RIGHT_DOWN);
                TriggerRegisterPlayerEvent(onKeyEvent, applyFor, EVENT_PLAYER_ARROW_RIGHT_UP);
                TriggerRegisterPlayerEvent(onKeyEvent, applyFor, EVENT_PLAYER_ARROW_LEFT_DOWN);
                TriggerRegisterPlayerEvent(onKeyEvent, applyFor, EVENT_PLAYER_ARROW_LEFT_UP);
                
                PlayerCameras[GetPlayerId(applyFor)] = this;
                
                registerKeyEvent(function()
                {
                    TriggerCam = PlayerCameras[GetPlayerId(GetTriggerPlayer())];
                
                    if (GetTriggerEventId() == EVENT_PLAYER_ARROW_UP_DOWN) {
                        TriggerCam.upPressed = true;
                    } else if (GetTriggerEventId() == EVENT_PLAYER_ARROW_UP_UP) {
                        TriggerCam.upPressed = false;
                    } else if (GetTriggerEventId() == EVENT_PLAYER_ARROW_DOWN_DOWN) {
                        TriggerCam.downPressed = true;
                    } else if (GetTriggerEventId() == EVENT_PLAYER_ARROW_DOWN_UP) {
                        TriggerCam.downPressed = false;
                    } else if (GetTriggerEventId() == EVENT_PLAYER_ARROW_RIGHT_DOWN) {
                        TriggerCam.rightPressed = true;
                    } else if (GetTriggerEventId() == EVENT_PLAYER_ARROW_RIGHT_UP) {
                        TriggerCam.rightPressed = false;
                    } else if (GetTriggerEventId() == EVENT_PLAYER_ARROW_LEFT_DOWN) {
                        TriggerCam.leftPressed = true;
                    } else if (GetTriggerEventId() == EVENT_PLAYER_ARROW_LEFT_UP) {
                        TriggerCam.leftPressed = false;
                    } 
                });
            }
        
            method movement()
            {
                real x, y, ox, oy, facing, movespeed;
                if (CanUnitMove(toFollow))
                {
                    x = GetUnitX(toFollow); y = GetUnitY(toFollow); facing = GetUnitFacing(toFollow)*bj_DEGTORAD;
                    ox = x; oy = y;
                    movespeed = GetUnitMoveSpeed(toFollow);
                
                    if (upPressed && !downPressed)
                    {
                        playAnimation = true;
                        
                        if (rightPressed && !leftPressed)
                        {
                            x += movespeed * LOOP_INTERVAL * Cos(facing - GetUnitDefaultTurnSpeed(toFollow));
                            y += movespeed * LOOP_INTERVAL * Sin(facing - GetUnitDefaultTurnSpeed(toFollow));
                        }
                        else if (!rightPressed && leftPressed)
                        {
                            x += movespeed * LOOP_INTERVAL * Cos(facing + GetUnitDefaultTurnSpeed(toFollow));
                            y += movespeed * LOOP_INTERVAL * Sin(facing + GetUnitDefaultTurnSpeed(toFollow));
                        }
                        else
                        {
                            x += movespeed * LOOP_INTERVAL * Cos(facing);
                            y += movespeed * LOOP_INTERVAL * Sin(facing);
                        }
                            
                        SetUnitPosition(toFollow, x, y);
                        
                        if (RAbsBJ(GetUnitX(toFollow) - x) > 0.5 || RAbsBJ(GetUnitY(toFollow) - y) > 0.5)
                        {
                            SetUnitPosition(toFollow, x, oy);
                            
                            if (RAbsBJ(GetUnitX(toFollow) - x) > 0.5)
                            {
                                SetUnitPosition(toFollow, ox, y);
                                
                                if (RAbsBJ(GetUnitY(toFollow) - y) > 0.5)
                                {
                                    SetUnitPosition(toFollow, x, y);
                                }
                            }
                        }
                    }
                    else
                    {
                        if (playAnimation)
                        {
                            playAnimation = false;
                            SetUnitAnimation(toFollow, "stand");
                        }
                    }
                    
                    if (rightPressed && !leftPressed)
                    {
                        if (TURN_ANGLE == 0)
                        {
                            SetUnitFacingTimed(toFollow, (facing - GetUnitDefaultTurnSpeed(toFollow)) * bj_RADTODEG, UPDATE_INTERVAL);
                        }
                        else
                        {
                            SetUnitFacingTimed(toFollow, (facing - TURN_ANGLE) * bj_RADTODEG, UPDATE_INTERVAL);
                        }
                    }
                    else if (!rightPressed && leftPressed)
                    {
                        if (TURN_ANGLE == 0)
                        {
                            SetUnitFacingTimed(toFollow, (facing + GetUnitDefaultTurnSpeed(toFollow)) * bj_RADTODEG, UPDATE_INTERVAL);
                        }
                        else
                        {
                            SetUnitFacingTimed(toFollow, (facing + TURN_ANGLE) * bj_RADTODEG, UPDATE_INTERVAL);
                        }
                    }
                    
                    animationCount += LOOP_INTERVAL;
                    
                    if (animationCount >= animationDur && playAnimation)
                    {
                        SetUnitAnimationByIndex(toFollow, animationIndex);
                        animationCount = 0;
                    }
                }
            }
            
            method registerKeyEvent(code cb) -> triggeraction
            {
                return TriggerAddAction(onKeyEvent, cb);
            }
            
            method unregisterKeyEvent(triggeraction ta)
            {
                TriggerRemoveAction(onKeyEvent, ta);
            }
        }
    }

//! endzinc

    native UnitAlive takes unit whichUnit returns boolean

Keywords:
camera, cam, camera system, cam system, key movement, key arrow, movement, system, 3rd person camera, third person camera
Contents

Cam System v3 (Map)

Reviews
15:34, 28th Nov 2009 TriggerHappy: Decent enough. Approved.

Moderator

M

Moderator

15:34, 28th Nov 2009
TriggerHappy:

Decent enough. Approved.
 
Level 9
Joined
Aug 27, 2009
Messages
473
OMG! This is epic, 5/5. Best CAM / Arrow Movement on the Hives!
Approve this please!

Only bad thing i saw: Selecting 2 units does not work.
Cant say anything about the Codings since im not a vJass / Jasser.. ^^

Im thinking about switching my old cam with this, but then i have to get it work for 10 players / 1 trigger each player. (So you can turn on / off)

EDIT:
+ rep ofc!
 
Level 11
Joined
Apr 29, 2007
Messages
826
Well yeah this is mostly designed for RPG's where you have control over only one hero.
About turning it on/off. I should add an option for that(just clearing the current unit should work)
Making it work for 10 players is easily, you just have to initialize them for each player.
Like this:
JASS:
    //! textmacro CAM_INIT_TRIGGER
        local integer i = 0
        loop
            exitwhen i > 9
            call Cam.create(i)
            set i = i+1
        endloop
    //! endtextmacro
And you're done :)

Update:
  • Added some new functions to ease the use for users which aren't very familier with (v)Jass. (Since it get's inlined anyway)
  • Added a Cam.switch method(or CamSwitch function) which will switch the camera for that player on or off
 
Last edited:
Level 10
Joined
Sep 21, 2007
Messages
517
interestingimo! i just started working on a cam system myself ^^ altho man there is just a tiny hint of advice i would like to say, i saw a comparison of z1 and z2, so im assuming its for cam offset, anyways bro, u should (if u didnt already do) im not sure since its too much code for me, partly b/c im lazy, like make a functioin that uses a global location and moving it around, it worked really well for me ^^

[offtopic] youready to pwn newbs now? )) [/offtopic]
 
Level 11
Joined
Apr 29, 2007
Messages
826
It does use a globals location and move it around :) Look at the code closer.
JASS:
    globals
        private location zloc = Location(0,0)
        //bla
    endglobals
Somewhere after all the configuration stuff.

JASS:
                call MoveLocation(zloc, GetUnitX(.current), GetUnitY(.current))
                set z1 = GetLocationZ(zloc)
                call MoveLocation(zloc, GetUnitX(.current)+.distance*Cos((GetUnitFacing(.current)-180)*bj_DEGTORAD), GetUnitY(.current)+.distance*Sin((GetUnitFacing(.current)-180)*bj_DEGTORAD))
                set z2 = GetLocationZ(zloc)
Right there in the cam trigger. And yes, that is to get the offset and also correct the angle(so you won't be stuck in steep cliffs)

[Offtopic] Yeah get your ass on Northrend! :p[/Offtopic]
 
Level 9
Joined
Aug 27, 2009
Messages
473
[*]Added a Cam.switch method(or CamSwitch function) which will switch the camera for that player on or off[/list]

The Escape button dont fit that..
And, when you turn on after turn off you still have unit as a selected unit, but you have to select it agein to turn the system realy on. Isnt it better to just un select units when switch off?
 
Level 9
Joined
Aug 27, 2009
Messages
473
You don't have to use the Escape button :) it's only a example. Just use any event you would like(ie Player types -cam off).
And that really depens on what people want. In my opinion it would be quiet confusing if your unit get's deselected.

Aha, didnt check triggers for this version.. I got lolled ^^
Can you explain how to add more people a bit easyer.
And how can i change the ability for changing Multiboards into a command?

Else, im gonna use this! :D
Good works, you can have your own Thanks to in the Quest for this, its awsome!
 
Level 9
Joined
Aug 27, 2009
Messages
473
No, there's nothing required. And I can't say much if I don't know what you exactly did. :/

I Runned the Map in World Editor (WE).
I used the button: "Test Map".
I Exited the map, and did go back to WE.
Then i did go to the "Trigger Editor".
Then i did go to the "sdj" Trigger.
There i Removed the Event.
And i added a new Event.

When i agein pressed the "Test Map" button, i got around 10-20 error massages and when the game finaly started, the triggers didnt work.
When i did go back, i saw that every vJass trigger was UnEnabled.

Other Usefull facts:
- It was on this Test Map.
- I only runned it once.
- I didnt do like in the Documentation, but i didnt think i did need to do that since it was in your own map.
Nothing more to add.

What to do?
 
Level 2
Joined
Sep 28, 2009
Messages
12
One small bug: the flying unit appears to vibrate slightly when turning. Is there an option in the unit art settings that can be changed to fix this?
 
Level 9
Joined
Oct 17, 2007
Messages
547
Can you add a GUI trigger to disable cam for a specific player?

For example, create a spell that when cast turns off the camera system for the owner of the unit, returning it to normal view and when it is cast again it will turn on the cam system for the owner of the unit.
 
Level 9
Joined
Oct 17, 2007
Messages
547
And how do I make an exeption to a unit type. For example if I click on a footman the cam will still remain at the last unit it won't switch over.
 
Level 9
Joined
Oct 17, 2007
Messages
547
How do I make only one unit type an exception? If i make it false then I would have to set up the cam for all the other units right?

Also how is the animation index is suppose to be counted? I start from the first stand animation and count to the walk but it give diff animation for each unit. For example whats the walking animation index for the villager kid 1?
 
Last edited:
Level 11
Joined
Apr 29, 2007
Messages
826
Well that basicly is possible with a few lines of code. You can turn off the click event and also the arrow movement, just look through the globals.

To set the current target of the camera, you'd just do:
JASS:
function bla takes nothing returns nothing
    local group g = CreateGroup()
    local unit u

    call GroupEnumUnitsInRange(0, 0, 99999, null)
    loop
        set u = FirstOfGroup(g)
        exitwhen u == null

        if /*Some Conditions of yours*/ then
            set Cam[GetOwningPlayer(u)].current = u
        endif

        call GroupRemoveUnit(g, u)
    endloop

    call DestroyGroup(g)
    set g = null
    set u = null
endfunction

This requires you to save the cam in a global array for each player, otherwise you can't really access it.
JASS:
globals
    integer array Cam
endglobals

function init takes nothing returns nothing
    local integer i = 0
    
    loop
        set Cam[i] = Cam.create(i)
        set i = i+1
        exitwhen i > 11
    endloop
endfunction
 
Level 1
Joined
Nov 30, 2009
Messages
3
hi Im a complete noob at this jass stuff..
tell me how to use this system for a map?

lets say for a RPG where the players select the hero at the beginning.. and the camera should be switching to your system... and this should happen to all 10 players when they select a hero.. also, the switching of cameras when selecting another unit should also be disabled..

sorry for asking a noob question.. i just dont understand!

thanx
 
Level 2
Joined
Mar 25, 2008
Messages
11
Hi! Great system, but i noticed that you couldn't go backwards or just turn around by pressing "down" buttom :cry: Otherwise a great system! 9/10
 
Level 2
Joined
Feb 25, 2010
Messages
10
the BEST cam system I ever seen!!!!! 10000/10000!!! ;P I've tried others and get mad becouse the normal attck will not work. with this cam system you can do that, + its so much better.

There are some things I need help with. When I add one of my character in the system (a human priest is the model) and animation 4. but when I walk, the animation does not play. But when I stop walking, the animation starts!? could you help me fix it?

there is one more thing, where do I change the animation speed? my animation is a little slow I think.

Hope you have answers to my questions and thanks for the ultimate camera system :)
 
Top