PFM: Mejorando la cámara en tercera persona

En su día ya hicimos una cámara en tercera persona. Sin embargo la cámara se siente un tanto rígida, pues sigue exactamente los movimientos del personaje:

Por eso la modificaremos para que su movimiento sea un poco más suave.

La cámara que creamos en su día se define por las siguientes posiciones, explicadas tras la imágen. Lo que haremos para que el movimiento sea suave es interpolar estos vectores a lo largo del tiempo, en lugar de aplicarlos instantáneamente.mejorandoCamara01

  • A: Es la posición inicial desde donde se calcula la posición de la cámara. La haremos coincidir con la posición del personaje.
  • B-A: Offset (desplazamiento) vertical aplicado a la posición inicial de la cámara. La cámara rotará en torno a ese punto.
  • C-B: Offset de la cámara. Lo que se aleja y desplaza la cámara con respecto al punto B.
  • D-B: Es el offset final después de multiplicar el offset indicado por un factor de escala, que actúa como Zoom.

Bien, el objetivo es entonces interpolar estos valores. Pasemos al código:

PFMPawn

Habrá que hacer algunas modificaciones en PFMPawn. En primer lugar habrá que sustituir las variables relacionadas con la cámara por las siguientes:


/******************************************************************************
Propiedades de Camara
******************************************************************************/
var vector DesiredCamStartLocation; // Posición deseada desde la que se hace el offset (Posición del Pawn)
var vector DesiredCamOffset;        // Offset deseado de la cámara
var float DesiredCamScale;          // CameraScale deseado
var float DesiredCamZOffset;        // Offset en z de la cámara (añadido al offset de cámara)

var vector CurrentCamStartLocation; // Posición actual desde la que se hace el offset
var vector CurrentCamOffset;        // Offset actual interpolado de la cámara
var float CurrentCamScale;          // Se usa para interpolar entre distintos CameraScales
var float CurrentCamZOffset;        // Usado para interpolar el offset en z de la cámara

var vector CamOffsetDefault;    // Offset de postura Default
var vector CamOffsetAimRight;   // Offset de postura AimRight

var float CameraInterpolationSpeed;     //Velocidad de interpolación

Como se ve hay 4 variables Desired y 4 Current. Las variables Desired representan el valor final de la cámara que queremos alcanzar, mientras que las variables Current almacenarán los valores actuales interpolados.

También hay un offset diferente según la postura, para que al sostener un arma se vea bien a dónde se apunta.

Ahora hay que modificar la función CalcCamera, que nunca me convenció del todo:


simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc,
out rotator out_CamRot, out float out_FOV )
{
    local vector HitLocation, HitNormal, CamDirX, CamDirY, CamDirZ;

    // Valores deseados:
    DesiredCamStartLocation = Location;  // El punto inicial de la cámara es la posición del Pawn
    if(WeaponType == PWT_AimRight)       // Offset varía según postura (tipo de arma)
    {
        DesiredCamOffset = CamOffsetAimRight;
    }
    else
    {
        DesiredCamOffset = CamOffsetDefault;
    }
    DesiredCamZOffset = (Health > 0) ? 1.0 * GetCollisionHeight() + Mesh.Translation.Z : 0.f;

    // Valores interpolados:
    CurrentCamStartLocation = VLerp(CurrentCamStartLocation, DesiredCamStartLocation, CameraInterpolationSpeed*fDeltaTime);
    CurrentCamOffset = VLerp(CurrentCamOffset, DesiredCamOffset, CameraInterpolationSpeed*fDeltaTime);
    CurrentCamScale = Lerp(CurrentCamScale, DesiredCamScale, CameraInterpolationSpeed*fDeltaTime);
    CurrentCamZOffset = Lerp(CurrentCamZOffset, DesiredCamZOffset, CameraInterpolationSpeed*fDeltaTime);

    if ( Health <= 0 )
    {
        CurrentCamOffset = vect(0,0,0);
        CurrentCamOffset.X = GetCollisionRadius();
    }

    // Se extraen los ejes de la rotación de la cámara
    GetAxes(out_CamRot, CamDirX, CamDirY, CamDirZ);
    // Escala de la camara (zoom)
    CamDirX *= CurrentCamScale;

    if ( (Health <= 0) || bFeigningDeath )
    {
        // adjust camera position to make sure it's not clipping into world
        // @todo fixmesteve.  Note that you can still get clipping if FindSpot fails (happens rarely)
        FindSpot(GetCollisionExtent(),CurrentCamStartLocation);
    }

    // Cálculo de la posición final de la cámara
    out_CamLoc = (CurrentCamStartLocation + vect(0.0,0.0,1.0) * CurrentCamZOffset) - CamDirX*CurrentCamOffset.X + CurrentCamOffset.Y*CamDirY + CurrentCamOffset.Z*CamDirZ;

    // Se traza un rayo para calcular posibles colisiones con la geometría
    if (Trace(HitLocation, HitNormal, out_CamLoc, CurrentCamStartLocation, false, vect(12,12,12)) != None)
    {
        out_CamLoc = HitLocation;
    }

    return true;
}

Se ha simplificado bastante, pues se divide en 3 etapas claras. Primero se calculan los vectores deseados, estos son los que queremos que la cámara obtenga finalmente. En la segunda etapa se calculan las posiciones interpoladas, que serán las que se apliquen en realidad a la cámara. Finalmente se calcula la posición de la cámara.

En la sección de defaultproperties también habrá que hacer las modificaciones pertinentes:


// Propiedades de cámara
DesiredCamScale=20.0
CurrentCamScale=0.0

CameraInterpolationSpeed=8.0              //Velocidad de interpolación de la cámara

CamOffsetDefault=(X=4.0,Y=0.0,Z=-13.0)     // Offset postura default
CamOffsetAimRight=(X=4.0,Y=40.0,Z=-13.0)    // Offset postura RightAim

Veamos el resultado:

Como puede verse la cámara se desplaza ahora mucho más suavemente, con un cierto retardo con respecto al personaje. Este retardo puede variarse modificando la velocidad de interpolación hasta en contrar un valor a nuestro gusto.
Banner Blog

Anuncios

54 pensamientos en “PFM: Mejorando la cámara en tercera persona

    • Lo tiene. En el código se usa la función Trace, que traza un rayo contra la geometría para calcular cualquier posible colisión.

    • ¿Cómo sería el comportamiento de la cámara cuando el personaje se pusiera de espaldas pegado a una pared?. ¿Estaría la cámara delante del personaje mirándole, o se acortaría el radio de la cámara hasta que su hubiera posicionado a la altura de la cabeza de nuestro personaje?

  1. Hola marcos. Antes de nada darte las gracias por hacer unos tutoriales tan buenos..
    He seguido tus tutoriales de la camara en tercera pesona con mi propio modelo ,,la verdad es que funciona de maravilla. el problema que tengo que el modelo va super rapido en el escenario hay alguna manera de controlar la veocidad de la camara y asi que vaya mas despacio el modelo sobre el escenario..
    Gracias.

    • Hola segio, me alegro que te sean útiles 🙂

      Es muy sencillo modificar la velocidad máxima del personaje, sólo tienes que modificar la variable GroundSpeed en la sección defaultproperties de tu clase Pawn (la que es mi PFMPawn). Así, tendrías que añadir en dicha sección una línea como esta:

      GroundSpeed=300

      Dándole el valor que se adecue a lo que quieras. Esta variable es la que determina la velocidad máxima que puede alcanzar el Pawn en carrera.

  2. ¿Hay alguna forma de obtener el valor del FOV de la cámara y luego cambiarlo?

    Sólo he visto que se pueda introducir vía consola, pero me gustaría poder cambiarlo al hacer las animaciones de correr y andar.

    Otra cosa parecida, ¿se puede consultar el tiempo que lleva en idle nuestro pawn y resetearlo?

    • Las funciones que se ejecutan en consola son en realidad funciones de código, así que pueden llamarse desde el código. En este caso sería la función FOV de PlayerController. Así que en mi PFMPlayerController se podría llamara la función FOV() en el momento que se quiera con el valor que se quiera para cambiarlo. PlayerController tiene varias variables relacionadas con el FOV: FOVAngle, DesiredFOV, DefaultFOV. FOVAngle debe ser la que guarda el FOV actual. Podría usarse el DesiredDov para interpolar el FOVAngle y que la transición sea suave, igual que hago con los vectores de la cámara en este tutorial. Creo que le pondré yo también el efecto al correr 😀

      Luego el tiempo en Idle por lo que he visto en PlayerController está la variable LastActiveTime que guarda el último momento de actividad y se usa para echar de las partidas a la gente inactiva. Podrías compararlo con el tiempo actual (WorldInfo.TimeSeconds) para saber el tiempo inactivo y decidir qué hacer.

  3. Tomo nota, porque de momento ni sé como manejar esas variables, me salen errores al colocarlas en el controler, o en el pawn. Supongo que habrá que declararlas. Tampoco sé cómo funciona el tema, si hay un bucle principal del juego, cada cuanto tiempo se comprueba, si hay que usar funciones, los “states”, etc… Cosas de empezar la casa por el tejado y no haber aprendido el lenguaje primero…

  4. muy buen blog !!!! pero creo q hasta aca llegue sin saber programacion..la venia careteando bien jaja , me da estos errores……..
    Unreal Frontend started 29/04/2013 19:00:02…
    [COMMANDLET ‘UDK.exe make’ STARTED IN ”] abr 29, 7:00
    Init: Version: 9505
    Init: Epic Internal: 0
    Init: Compiled (32-bit): Feb 24 2012 11:59:28
    Init: Command line:
    Init: Base directory: C:\UDK\UDK-2012-02\Binaries\Win32\
    Init: Character set: Unicode
    Log: Executing Class UnrealEd.MakeCommandlet
    ——————–Core – Release——————–
    ——————–Engine – Release——————–
    ——————–IpDrv – Release——————–
    ——————–GFxUI – Release——————–
    ——————–GameFramework – Release——————–
    ——————–UnrealEd – Release——————–
    ——————–GFxUIEditor – Release——————–
    ——————–WinDrv – Release——————–
    ——————–OnlineSubsystemPC – Release——————–
    ——————–OnlineSubsystemSteamworks – Release——————–
    ——————–OnlineSubsystemGameCenter – Release——————–
    ——————–UDKBase – Release——————–
    ——————–UTEditor – Release——————–
    ——————–UTGame – Release——————–
    ——————–UTGameContent – Release——————–
    ——————–PFM – Release——————–
    Analyzing…
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMPawn.uc(125) : Error, Bad or missing expression in ‘If’
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMPawn.uc(70) : Error, ‘DesiredAim’: Bad command or expression
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMPawn.uc(60) : Error, ‘PFMAimNode’: Bad command or expression
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMPawn.uc(54) : Error, ‘PFMAimNode’: Bad command or expression
    Compile aborted due to errors.
    Warning/Error Summary
    ———————
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMPawn.uc(125) : Error, Bad or missing expression in ‘If’
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMPawn.uc(70) : Error, ‘DesiredAim’: Bad command or expression
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMPawn.uc(60) : Error, ‘PFMAimNode’: Bad command or expression
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMPawn.uc(54) : Error, ‘PFMAimNode’: Bad command or expression
    Failure – 4 error(s), 0 warning(s)
    Execution of commandlet took: 17.80 seconds
    [abr 29, 7:00 ] COMMANDLET ‘UDK.exe make’ FAILED

    y aca esta mi PFMPawn:

    class PFMPawn extends UDKPawn;

    /******************************************************************************
    Propiedades de Camara
    ******************************************************************************/
    var vector DesiredCamStartLocation; // Posición deseada desde la que se hace el offset (Posición del Pawn)
    var vector DesiredCamOffset; // Offset deseado de la cámara
    var float DesiredCamScale; // CameraScale deseado
    var float DesiredCamZOffset; // Offset en z de la cámara (añadido al offset de cámara)

    var vector CurrentCamStartLocation; // Posición actual desde la que se hace el offset
    var vector CurrentCamOffset; // Offset actual interpolado de la cámara
    var float CurrentCamScale; // Se usa para interpolar entre distintos CameraScales
    var float CurrentCamZOffset; // Usado para interpolar el offset en z de la cámara

    var vector CamOffsetDefault; // Offset de postura Default
    var vector CamOffsetAimRight; // Offset de postura AimRight

    var float CameraInterpolationSpeed; //Velocidad de interpolación

    /* Iluminación de entorno */

    var DynamicLightEnvironmentComponent LightEnvironment;

    // Sobreescribe la funcion de Pawn
    function AddDefaultInventory()
    {
    // Se le da al Pawn el arma al comenzar
    InvManager.CreateInventory(class’PFM.PFMWeapon1′);
    InvManager.CreateInventory(class’PFM.PFMWeapon2′);
    }

    // Cambio de arma, llamado desde PFMPlayerController
    simulated function SwitchWeapon(byte NewGroup)
    {
    if (PFMInventoryManager(InvManager) != None)
    {
    // Se le indica al Manager del Inventario que cambie de arma
    PFMInventoryManager(InvManager).SwitchWeapon(NewGroup);
    }
    }

    // Sobreescribe una función para que se vea al pawn por defecto
    // La llama la Camara cuando este actor se convierte en su ViewTarget

    simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
    {
    // Se busca la referencia del nodo de Aiming
    PFMAimNode = AnimNodeAimOffset(SkelComp.FindAnimNode(‘PFMAimNode’));
    }

    simulated event Destroyed()
    {
    Super.Destroyed();
    PFMAimNode = None; //Es necesario desreferenciar el nodo para que el Pawn se destruya correctamente
    }

    simulated function Tick(float DeltaTime)
    {
    Super.Tick(DeltaTime);

    // Se obtiene la rotación actual del controller y se clampea con el max y min
    DesiredAim = GetBaseAimRotation();
    DesiredAim.Pitch = Clamp(DesiredAim.Pitch, ViewPitchMin, ViewPitchMax);

    // Interpolación de la rotación
    if (DesiredAim != CurrentAim)
    {
    CurrentAim = RLerp(CurrentAim, DesiredAim, AimSpeed * DeltaTime, false);
    }

    // Se cambia el nodo de animación
    if (CurrentAim.Pitch 0)
    {
    PFMAimNode.Aim.Y = float(CurrentAim.Pitch) / ViewPitchMax;
    }
    else
    {
    PFMAimNode.Aim.Y = 0.f;
    }
    }

    simulated event BecomeViewTarget( PlayerController PC )
    {
    local UTPlayerController UTPC;

    // Por defecto esta llamada pone los brazos al Pawn y el arma inicial (y otras cosas)
    Super.BecomeViewTarget(PC);

    if (LocalPlayer(PC.Player) != None)
    {
    UTPC = UTPlayerController(PC);
    if (UTPC != None)
    {
    // Activa la vista trasera (Poner la camara en tercera persona y hace desaparecer brazos y armas de primera persona
    UTPC.SetBehindView(true);
    // Esto no es necesario
    //SetMeshVisibility(UTPC.bBehindView);
    }
    }
    }

    simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc,
    out rotator out_CamRot, out float out_FOV )
    {
    local vector HitLocation, HitNormal, CamDirX, CamDirY, CamDirZ;

    // Valores deseados:
    DesiredCamStartLocation = Location; // El punto inicial de la cámara es la posición del Pawn
    if(WeaponType == PWT_AimRight) // Offset varía según postura (tipo de arma)
    {
    DesiredCamOffset = CamOffsetAimRight;
    }
    else
    {
    DesiredCamOffset = CamOffsetDefault;
    }
    DesiredCamZOffset = (Health > 0) ? 1.0 * GetCollisionHeight() + Mesh.Translation.Z : 0.f;

    // Valores interpolados:
    CurrentCamStartLocation = VLerp(CurrentCamStartLocation, DesiredCamStartLocation, CameraInterpolationSpeed*fDeltaTime);
    CurrentCamOffset = VLerp(CurrentCamOffset, DesiredCamOffset, CameraInterpolationSpeed*fDeltaTime);
    CurrentCamScale = Lerp(CurrentCamScale, DesiredCamScale, CameraInterpolationSpeed*fDeltaTime);
    CurrentCamZOffset = Lerp(CurrentCamZOffset, DesiredCamZOffset, CameraInterpolationSpeed*fDeltaTime);

    if ( Health <= 0 )
    {
    CurrentCamOffset = vect(0,0,0);
    CurrentCamOffset.X = GetCollisionRadius();
    }

    // Se extraen los ejes de la rotación de la cámara
    GetAxes(out_CamRot, CamDirX, CamDirY, CamDirZ);
    // Escala de la camara (zoom)
    CamDirX *= CurrentCamScale;

    if ( (Health <= 0) || bFeigningDeath )
    {
    // adjust camera position to make sure it's not clipping into world
    // @todo fixmesteve. Note that you can still get clipping if FindSpot fails (happens rarely)
    FindSpot(GetCollisionExtent(),CurrentCamStartLocation);
    }

    // Cálculo de la posición final de la cámara
    out_CamLoc = (CurrentCamStartLocation + vect(0.0,0.0,1.0) * CurrentCamZOffset) – CamDirX*CurrentCamOffset.X + CurrentCamOffset.Y*CamDirY + CurrentCamOffset.Z*CamDirZ;

    // Se traza un rayo para calcular posibles colisiones con la geometría
    if (Trace(HitLocation, HitNormal, out_CamLoc, CurrentCamStartLocation, false, vect(12,12,12)) != None)
    {
    out_CamLoc = HitLocation;
    }

    return true;
    }

    defaultproperties
    {

    AimSpeed=8
    // Iluminación de entorno del Pawn
    Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
    bSynthesizeSHLight=false
    bIsCharacterLightEnvironment=false
    bUseBooleanEnvironmentShadowing=FALSE
    InvisibleUpdateTime=1
    MinTimeBetweenFullUpdates=.2
    End Object
    Components.Add(MyLightEnvironment)
    LightEnvironment=MyLightEnvironment

    // Componente SkeletalMesh para el robot
    Begin Object Class=SkeletalMeshComponent Name=SkeletalMeshComponentRobot
    SkeletalMesh=SkeletalMesh'Soldier_Assets.Maniqui_1'
    AnimSets(0)=AnimSet'Soldier_Assets.Maniqui_AnimSet'
    AnimTreeTemplate=AnimTree'Soldier_Assets.Maniqui_animtree'
    LightEnvironment=MyLightEnvironment
    bEnableSoftBodySimulation=True
    bSoftBodyAwakeOnStartup=True
    bAcceptsLights=True
    End Object
    Mesh=SkeletalMeshComponentRobot
    Components.Add(SkeletalMeshComponentRobot)

    // Se hace más pequeño el cilindro de colisión
    Begin Object Name=CollisionCylinder
    CollisionRadius=+0021.000000
    CollisionHeight=+0044.000000
    End Object
    CylinderComponent=CollisionCylinder

    // Propiedades de cámara
    DesiredCamScale=20.0
    CurrentCamScale=0.0

    CameraInterpolationSpeed=8.0 //Velocidad de interpolación de la cámara

    CamOffsetDefault=(X=4.0,Y=0.0,Z=-13.0) // Offset postura default
    CamOffsetAimRight=(X=4.0,Y=40.0,Z=-13.0) // Offset postura RightAim
    //Locomoción
    JumpZ=+0700.000000
    MaxFallSpeed=+1200.0 //Máxima velocidad al caer sin hacerse daño
    AirControl=+0.1 //Control aéreo
    CustomGravityScaling=1.3 //Multiplicador de gravedad personal

    InventoryManagerClass=class'PFM.PFMInventoryManager'
    } si le encontras la solucion voy a tratar de seguir hasta q no me de mas jaja gracias!!

    • Hola Yael! Por los errores que te salen me parece que al copiar el código has eliminado las variables DesiredAim y PFMAimNode que estaban en la parte de arriba. En este artículo hay que sustituir las variables de cámara anteriores por las nuevas pero DesiredAim y PFMAimNode no hay que eliminarlas! 😉

  5. wow gracias por la pronta respuesta!! pude solucionar esos errores pero me tiro un nuevo error
    Unreal Frontend started 29/04/2013 21:36:52…
    [COMMANDLET ‘UDK.exe make’ STARTED IN ”] abr 29, 9:37
    Init: Version: 9505
    Init: Epic Internal: 0
    Init: Compiled (32-bit): Feb 24 2012 11:59:28
    Init: Command line:
    Init: Base directory: C:\UDK\UDK-2012-02\Binaries\Win32\
    Init: Character set: Unicode
    Log: Executing Class UnrealEd.MakeCommandlet
    ——————–Core – Release——————–
    ——————–Engine – Release——————–
    ——————–IpDrv – Release——————–
    ——————–GFxUI – Release——————–
    ——————–GameFramework – Release——————–
    ——————–UnrealEd – Release——————–
    ——————–GFxUIEditor – Release——————–
    ——————–WinDrv – Release——————–
    ——————–OnlineSubsystemPC – Release——————–
    ——————–OnlineSubsystemSteamworks – Release——————–
    ——————–OnlineSubsystemGameCenter – Release——————–
    ——————–UDKBase – Release——————–
    ——————–UTEditor – Release——————–
    ——————–UTGame – Release——————–
    ——————–UTGameContent – Release——————–
    ——————–PFM – Release——————–
    Analyzing…
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMPawn.uc(130) : Error, Bad or missing expression in ‘If’
    Compile aborted due to errors.
    Warning/Error Summary
    ———————
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMPawn.uc(130) : Error, Bad or missing expression in ‘If’
    Failure – 1 error(s), 0 warning(s)
    Execution of commandlet took: 13.56 seconds
    [abr 29, 9:37 ] COMMANDLET ‘UDK.exe make’ FAILED

    SE REFIERE A ESTA LINEA:
    if(WeaponType == PWT_AimRight) // Offset varía según postura (tipo de arma)
    creo que cai en el mismo error de las variables y he borrado algo q no debia ^^
    el copy past entra a fallar ajaj.. GRACIAS!!

  6. a.. te cuento que borre desde
    simulated function bool CalcCamera etc.. hasta
    return true;
    }
    y copie el tuyo en ese lugar por ahi debe estar el error..Gracias

    • Creo que también borraste la variable WeaponType y por eso no la encuentra. Asegúrate que tienes esto en la parte de arriba de tu código:

      // Posturas para disparo y aiming:
      enum PawnWeaponType
      {
      PWT_Default,
      PWT_AimRight
      };

      var PawnWeaponType WeaponType; // Postura actual

      O si no vas a utilizar las diferentes cámaras dependiendo del tipo de arma, puedes en su lugar borrar todo el bloque de lineas que dan el error:

      if(WeaponType == PWT_AimRight) // Offset varía según postura (tipo de arma)
      {
      DesiredCamOffset = CamOffsetAimRight;
      }
      else
      {
      DesiredCamOffset = CamOffsetDefault;
      }

      Y dejando sólo

      DesiredCamOffset = CamOffsetDefault;

  7. Perfecto con eso se arreglo !! disculpa los mensages extensos
    por ahora la voy llevando ajaj

    creo que las armas grandes y que el personaje por momentos no se vea es por que me quedo chico y a los pajarracos los tengo que empujar ajaj.. no se si no le da bola a PFMScout o si tengo que modificarle los PathSizes

    • Tiene buena pinta! para hacer las armas más pequeñas ve a sus respectivos códigos y disminuye el valor de Scale en defaultproperties.

      Lo de que se atasquen los enemigos puedes comprobar si ocurre con un pasillo algo más ancho pues ese parece un poco estrecho y se quedan atascados.

  8. otro problema que tengo es que no puedo elegir el no estar con armas, me pasa de la primera arma a la segunda ..
    no se si es algun problema en el PFMInventoryManager o PFMWeapon..
    probe la mejora de rotancion del pj y funciona perfecto salvo este problema.
    Gracias!!

    • Al pasar con la rueda de ratón por defecto se recorren todas las armas. Si quieres guardar el arma tendrás que programarlo aparte. Puedes hacer que con alguna tecla el PFMInventoryManager quite el arma actual sin sacar una nueva.

  9. Ok lo pude solucionar via kismet

    a si queda si desactivo el AnimNodePerBone.. si lo activo sigue apuntando con o sin arma
    alguna idea de donde puede llegar a estar el problema ?

  10. claro.. pase por alto esta parte !! ahi arregle el AnimTree y el codigo pero me tira un error que no puedo solucionar
    Unreal Frontend started 03/05/2013 13:50:49…
    [COMMANDLET ‘UDK.exe make’ STARTED IN ”] may 3, 1:51
    Init: Version: 9505
    Init: Epic Internal: 0
    Init: Compiled (32-bit): Feb 24 2012 11:59:28
    Init: Command line:
    Init: Base directory: C:\UDK\UDK-2012-02\Binaries\Win32\
    Init: Character set: Unicode
    Log: Executing Class UnrealEd.MakeCommandlet
    ——————–Core – Release——————–
    ——————–Engine – Release——————–
    ——————–IpDrv – Release——————–
    ——————–GFxUI – Release——————–
    ——————–GameFramework – Release——————–
    ——————–UnrealEd – Release——————–
    ——————–GFxUIEditor – Release——————–
    ——————–WinDrv – Release——————–
    ——————–OnlineSubsystemPC – Release——————–
    ——————–OnlineSubsystemSteamworks – Release——————–
    ——————–OnlineSubsystemGameCenter – Release——————–
    ——————–UDKBase – Release——————–
    ——————–UTEditor – Release——————–
    ——————–UTGame – Release——————–
    ——————–UTGameContent – Release——————–
    ——————–PFM – Release——————–
    Analyzing…
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMWeapon.uc(77) : Error, Type mismatch in Call to ‘SetWeaponType’, parameter 1
    Compile aborted due to errors.
    Warning/Error Summary
    ———————
    C:\UDK\UDK-2012-02\Development\Src\PFM\Classes\PFMWeapon.uc(77) : Error, Type mismatch in Call to ‘SetWeaponType’, parameter 1
    Failure – 1 error(s), 0 warning(s)
    Execution of commandlet took: 17.22 seconds
    [may 3, 1:51 ] COMMANDLET ‘UDK.exe make’ FAILED

    SE REFIERE A ESTA LINEA :
    // Se cambia la postura
    PFMPawn(Instigator).SetWeaponType(WeaponType);
    si borro esa linea no me tira error.. pero sigue sacudiendo el arma por todos lados ^^
    Disculpa los extensas preguntas..pero creo que si soluciono esto voy a poder seguir el tutorial sin problemas.. creo ^^ Gracias !!

    • Claro esa línea es justo la que cambia la postura del Pawn y por tanto hace que se desactive el AnimNodeBlendPerBone. Es raro que te salga ese error, comprueba que hayas seguido todos los pasos y no te falte por declarar alguna variable. Si no ves el fallo ponme el código que tengas y le echaré un vistazo.


  11. solucionado! estaba ubicando mal el codigo..
    lo que pasa es que apreto la tecla 0 como dice en el InventoryManager para que vuelva a estar sin arma pero no pasa nada,
    cambie de tecla pero lo mismo.. no guarda el arma

  12. si encontras que puede llegar a ser por favor tirame una idea, igual creo que asi puedo seguir el tutorial sin problemas no ?

    • Sin ver el código no se me ocurre qué puede pasar, será alguna tontería. De todas maneras sí puedes seguir con el resto de tutoriales sin problemas.

  13. http://postimg.org/image/6lsdyf5ef/ OK te dejo la imagen del animtree
    /////////////////////////////////////////////////
    el codigo del InventoryManger :
    ////////////////////////////////////////////////
    class PFMInventoryManager extends InventoryManager;

    // Cambia de arma. Llamado desde PFMPawn
    simulated function SwitchWeapon(byte NewGroup)
    {
    local PFMWeapon NewWeapon;

    if(NewGroup == 10) // Sin armas (el 10 es el grupo de la tecla 0)
    {
    SetCurrentWeapon(None); // Sin arma
    PFMPawn(Instigator).SetDefaultWeaponType(); // Postura por defecto sin arma
    return;
    }

    NewWeapon = GetWeaponByGroup(NewGroup);

    if(NewWeapon != none)
    {
    SetCurrentWeapon(NewWeapon);
    }
    }

    // Devuelve el primer arma que se encuente que coincida con el grupo de inventario
    function PFMWeapon GetWeaponByGroup(byte InventoryGroup)
    {
    local PFMWeapon Weap;

    ForEach InventoryActors(class’PFMWeapon’, Weap)
    {
    if(Weap.InventoryGroup == InventoryGroup)
    {
    return Weap;
    }
    }
    return none;

    }

    defaultproperties
    {
    // Se añade una entrada al array PendingFire del InventoryManager por cada tipo de disparo
    PendingFire(0)=0
    PendingFire(1)=0

    }

    //////////////////////////////////////////////////////////////////////////////////
    y el PFMWeapon :
    ///////////////////////////////////////////////////////////////

    class PFMWeapon extends UDKWeapon;

    var byte InventoryGroup; // Grupo de inventario. Se usa para cambiar de arma

    // ****************************************************************
    // Instant HIT
    // ****************************************************************
    var ParticleSystem BeamTemplate; // Sistema de partículas del rayo mostrado en Instant Hit

    var MaterialInterface ExplosionDecal; // Decal (calcomanía) que deja en las superficies el Instant Hit
    var float DecalWidth, DecalHeight; // Ancho y altura del decal
    var float DurationOfDecal; // Duración del decal antes de desaparecer
    var name DecalDissolveParamName; // Nombre del parámetro del MaterialInstance para disolver el material

    var PawnWeaponType WeaponType; // Tipo de arma para cambiar la postura de apuntado

    // Determina la posición real del arma
    simulated event SetPosition(UDKPawn Holder)
    {
    local SkeletalMeshComponent compo;
    local SkeletalMeshSocket socket;
    local Vector FinalLocation;

    compo = Holder.Mesh;

    if (compo != none)
    {
    socket = compo.GetSocketByName(‘Mano’);
    if (socket != none)
    {
    FinalLocation = compo.GetBoneLocation(socket.BoneName);
    }
    }
    SetLocation(FinalLocation);
    }

    // Calcula la posición real desde donde se disparan los proyectiles
    simulated event vector GetPhysicalFireStartLoc(optional vector AimDir)
    {
    local SkeletalMeshComponent compo;
    local SkeletalMeshSocket socket;

    compo = Instigator.Mesh;

    if (compo != none)
    {
    socket = compo.GetSocketByName(‘Mano’);
    if (socket != none)
    {
    return compo.GetBoneLocation(socket.BoneName);
    }
    }
    }

    // Se llama al equipar el arma
    simulated function TimeWeaponEquipping()
    {
    // Se acopla el arma al pawn
    AttachWeaponTo( Instigator.Mesh,’Mano’ );

    // Se cambia la postura
    PFMPawn(Instigator).SetWeaponType(WeaponType);

    super.TimeWeaponEquipping();
    }

    // Sobreescribe la funcion de Weapon. Procesa los impactos instantaneos.
    simulated function ProcessInstantHit(byte FiringMode, ImpactInfo Impact, optional int NumHits)
    {
    local MaterialInstanceTimeVarying MITV_Decal;

    // Calcula el daño total y lo aplica al actor
    Super.ProcessInstantHit(FiringMode, Impact, NumHits);

    // Creación del Decal
    MITV_Decal = new(self) class’MaterialInstanceTimeVarying’;
    MITV_Decal.SetParent(ExplosionDecal);
    WorldInfo.MyDecalManager.SpawnDecal(MITV_Decal, Impact.HitLocation, rotator(-Impact.HitNormal), DecalWidth, DecalHeight, 10.0, FALSE );
    MITV_Decal.SetScalarStartTime( DecalDissolveParamName, DurationOfDecal );

    // Generación del rayo de Instant Hit
    SpawnBeam(GetPhysicalFireStartLoc() + (FireOffset >> Instigator.GetViewRotation()), Impact.HitLocation, false);
    }

    // Acopla el arma a la malla del pawn
    simulated function AttachWeaponTo( SkeletalMeshComponent MeshComp, optional Name SocketName )
    {
    MeshComp.AttachComponentToSocket(Mesh,SocketName);
    // Se coge la iluminación de entorno del Pawn
    Mesh.SetLightEnvironment(PFMPawn(Instigator).LightEnvironment);
    }

    // Llamado al dejar de usar el arma
    simulated function DetachWeapon()
    {
    Instigator.Mesh.DetachComponent(Mesh);
    SetBase(None);
    SetHidden(True);
    }

    // Genera el rayo del arma. Extraído de UTAttachment_ShockRifle
    simulated function SpawnBeam(vector Start, vector End, bool bFirstPerson)
    {
    local ParticleSystemComponent E;
    local actor HitActor;
    local vector HitNormal, HitLocation;

    if (End == Vect(0,0,0))
    {
    if (!bFirstPerson || (Instigator.Controller == None))
    {
    return;
    }
    End = Start + vector(Instigator.Controller.Rotation) * class’UTWeap_ShockRifle’.default.WeaponRange;
    HitActor = Instigator.Trace(HitLocation, HitNormal, End, Start, TRUE, vect(0,0,0),, TRACEFLAG_Bullet);
    if ( HitActor != None )
    {
    End = HitLocation;
    }
    }
    E = WorldInfo.MyEmitterPool.SpawnEmitter(BeamTemplate, Start);
    E.SetVectorParameter(‘ShockBeamEnd’, End);
    if (bFirstPerson && !class’Engine’.static.IsSplitScreen())
    {
    E.SetDepthPriorityGroup(SDPG_Foreground);
    }
    else
    {
    E.SetDepthPriorityGroup(SDPG_World);
    }
    }

    defaultproperties
    {
    FireOffset=(X=50,Y=0,Z=0)

    // Tipo de postura
    WeaponType=PWT_AimRight

    }
    /////////////////////////////////////////////// Si encontras algun detalle joya !

  14. hola, gracias por los tutoriales, tengo un problema, al compilar el codigo no me marca error pero al probarlo me ase un efecto el cual la camara se aleja y se aserca , no se si el codigo este mal
    class PruebaPawn extends UTPawn;

    /******************************************************************************
    Propiedades de Camara
    ******************************************************************************/
    // Posturas para disparo y aiming:
    enum PawnWeaponType
    {
    PWT_Default,
    PWT_AimRight
    };

    var PawnWeaponType WeaponType; // Postura actual

    var vector DesiredCamStartLocation; // Posición deseada desde la que se hace el offset (Posición del Pawn)
    var vector DesiredCamOffset; // Offset deseado de la cámara
    var float DesiredCamScale; // CameraScale deseado
    var float DesiredCamZOffset; // Offset en z de la cámara (añadido al offset de cámara)

    var vector CurrentCamStartLocation; // Posición actual desde la que se hace el offset
    var vector CurrentCamOffset; // Offset actual interpolado de la cámara
    var float CurrentCamScale; // Se usa para interpolar entre distintos CameraScales
    var float CurrentCamZOffset; // Usado para interpolar el offset en z de la cámara

    var vector CamOffsetDefault; // Offset de postura Default
    var vector CamOffsetAimRight; // Offset de postura AimRight

    var float CameraInterpolationSpeed; //Velocidad de interpolación

    //Nota
    /*Como se ve hay 4 variables Desired y 4 Current. Las variables Desired representan el valor final de la cámara que queremos alcanzar,
    mientras que las variables Current almacenarán los valores actuales interpolados.
    También hay un offset diferente según la postura, para que al sostener un arma se vea bien a dónde se apunta.*/

    // Sobreescribe una función para que se vea al pawn por defecto
    // La llama la Camara cuando este actor se convierte en su ViewTarget

    simulated event BecomeViewTarget( PlayerController PC )
    {
    local UTPlayerController UTPC;

    // Por defecto esta llamada pone los brazos al Pawn y el arma inicial (y otras cosas)
    Super.BecomeViewTarget(PC);

    if (LocalPlayer(PC.Player) != None)
    {
    UTPC = UTPlayerController(PC);
    if (UTPC != None)
    {
    // Activa la vista trasera (Poner la camara en tercera persona y hace desaparecer brazos y armas de primera persona
    UTPC.SetBehindView(true);
    // Esto no es necesario
    //SetMeshVisibility(UTPC.bBehindView);
    }
    }
    }

    //CAMARA

    simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc,out rotator out_CamRot, out float out_FOV )
    {
    local vector HitLocation, HitNormal, CamDirX, CamDirY, CamDirZ;

    // Valores deseados:
    DesiredCamStartLocation = Location; // El punto inicial de la cámara es la posición del Pawn
    /*if(WeaponType == PWT_AimRight) // Offset varía según postura (tipo de arma)
    {
    DesiredCamOffset = CamOffsetAimRight;
    }
    else
    {*/
    DesiredCamOffset = CamOffsetDefault;
    //}
    DesiredCamZOffset = (Health > 0) ? 1.0 * GetCollisionHeight() + Mesh.Translation.Z : 0.f;

    // Valores interpolados:
    CurrentCamStartLocation = VLerp(CurrentCamStartLocation, DesiredCamStartLocation, CameraInterpolationSpeed*fDeltaTime);
    CurrentCamOffset = VLerp(CurrentCamOffset, DesiredCamOffset, CameraInterpolationSpeed*fDeltaTime);
    CurrentCamScale = Lerp(CurrentCamScale, DesiredCamScale, CameraInterpolationSpeed*fDeltaTime);
    CurrentCamZOffset = Lerp(CurrentCamZOffset, DesiredCamZOffset, CameraInterpolationSpeed*fDeltaTime);

    if ( Health <= 0 )
    {
    CurrentCamOffset = vect(0,0,0);
    CurrentCamOffset.X = GetCollisionRadius();
    }

    // Se extraen los ejes de la rotación de la cámara
    GetAxes(out_CamRot, CamDirX, CamDirY, CamDirZ);
    // Escala de la camara (zoom)
    CamDirX *= CurrentCamScale;

    if ( (Health <= 0) || bFeigningDeath )
    {
    // adjust camera position to make sure it's not clipping into world
    // @todo fixmesteve. Note that you can still get clipping if FindSpot fails (happens rarely)
    FindSpot(GetCollisionExtent(),CurrentCamStartLocation);
    }

    // Cálculo de la posición final de la cámara
    out_CamLoc = (CurrentCamStartLocation + vect(0.0,0.0,1.0) * CurrentCamZOffset) – CamDirX*CurrentCamOffset.X + CurrentCamOffset.Y*CamDirY + CurrentCamOffset.Z*CamDirZ;

    // Se traza un rayo para calcular posibles colisiones con la geometría
    if (Trace(HitLocation, HitNormal, out_CamLoc, CurrentCamStartLocation, false, vect(12,12,12)) != None)
    {
    out_CamLoc = HitLocation;
    }

    return true;
    }

    //propiedades de la camara

    defaultproperties
    {
    //Determina el 'zoom' de la cámara
    //CameraScale=40.0
    //40
    // Propiedades de cámara
    DesiredCamScale=20.0
    CurrentCamScale=0.0

    CameraInterpolationSpeed=8.0 //Velocidad de interpolación de la cámara

    CamOffsetDefault=(X=4.0,Y=0.0,Z=-13.0) // Offset postura default
    CamOffsetAimRight=(X=4.0,Y=40.0,Z=-13.0) // Offset postura RightAim

    //Modifica la velocidad del pawn
    //GroundSpeed=200

    }

  15. hola amigo,felicitaciones por tus esfuersos espero sigas terminando tu juego ronin ejeje esta la raja el video final que has puesto, y queria preguntarte donde pego el codigo de calccamera no logro hacer que funcione al compilarlo, cree un archivo “calccamera.uc” pero no pasa nada

  16. Me gustaría hacerte una pregunta relacionada con el tema de la cámara. ¿Hay alguna función en UDK para hacer que la cámara apunte hacia otro pawn? Poniendolo de otra manera, lo que sería el fijar blanco en juegos modernos. La cámara en base al pawn, pero mirando hacia un pawn enemigo.
    Sé que en Kismet hay algo similar, pero no sé el código correspondiente en unrealscript.
    ¡Gracias por la ayuda, Marcos!

  17. Hola Marcos tengo una gran duda. como puedo cambiar el DepthPriorityGroup en pleno juego ?
    e programado un poco y esto es lo que tengo.
    class efectoF extends KAsset
    Placeable;
    var (efectoF) editconst SkeletalMeshComponent personaje;

    simulated function cliked(){

    }

    exec function efectoOn(){

    //SDPG_Foreground;
    `log(“hola”);
    personaje.SetDepthPriorityGroup(SDPG_Foreground);

    }

    simulated function efectoOf(){

    // SDPG_World;
    `log(“adios”);
    personaje.SetDepthPriorityGroup(SDPG_World);

    }

    DefaultProperties
    {
    BlockRigidBody=true

    Begin Object Name=MyLightEnvironment
    bEnabled=true
    End Object

    Begin Object Class= SkeletalMeshComponent Name=KAssetSkelMeshComponent

    Animations=None
    SkeletalMesh=SkeletalMesh’UTExampleCrowd.Mesh.SK_Crowd_Robot’
    bHasPhysicsAssetInstance=true
    bUpdateKinematicBonesFromAnimation=true
    bUpdateJointsFromAnimation=true
    bSkipAllUpdateWhenPhysicsAsleep=TRUE
    bBlockFootPlacement=false
    PhysicsWeight=0.5

    BlockRigidBody=true
    CollideActors=true
    BlockActors=true
    BlockZeroExtent=true
    BlockNonZeroExtent=false
    RBChannel=RBCC_GameplayPhysics
    RBCollideWithChannels=(Default=TRUE,BlockingVolume=TRUE,GameplayPhysics=TRUE,EffectPhysics=TRUE)
    RBDominanceGroup=30
    DepthPriorityGroup=SDPG_World
    LightingChannels=(Dynamic=TRUE,Gameplay_1=TRUE) Rotation=(Yaw=0)

    End Object
    personaje = KAssetSkelMeshComponent

    }
    ———————
    class VPawn extends UDKPawn
    notplaceable;

    var PostProcessChain efecto;
    var bool efectoAct;
    var bool befectoToggle;
    var float LastPressedMyButtonTime;
    var LocalPlayer LocalPlayer;
    var efectoF actor1;

    simulated exec function ToggleEfecto()
    {

    SetTimer(0.1,false,’LetNVToggle’);

    }

    function LetNVToggle()
    {
    //befectoToggle=true;

    actor1 = Spawn(Class’efectoF’);
    LocalPlayer = LocalPlayer(GetALocalPlayerController().Player);
    LocalPlayer.InsertPostProcessingChain(efecto,0,true);
    actor1.efectoOn();
    `log(“hola”);
    efectoAct=true;
    }

    simulated exec function UnToggleEfecto()
    {
    // local LocalPlayer LocalPlayer;

    actor1 = Spawn(Class’efectoF’);
    LocalPlayer = LocalPlayer(GetALocalPlayerController().Player);
    LocalPlayer.RemovePostProcessingChain(0);
    actor1.efectoOf();
    `log(“adios…”);
    efectoAct=false;
    }

    DefaultProperties
    {

    efectoAct=false
    efecto=PostProcessChain’MyPackage.disolver_material.vicion’
    //NightVisionOn=SoundCue’SO_Sounds_Weapons.NightVisionOn_Cue’
    befectoToggle=true
    }

    gracias y espero tu respuesta

  18. Hola que tal, disculpame que te moleste, pero estoy dando vueltas por tu blog y me parece muy interesante, me gustaria preguntarte como podria modificar la camara para hacer un juego de plataformas tipo rayman legends.
    Recien estoy empezando por lo que estoy un poco perdido, te pido disculpas.

    Saludos

  19. Sí , gracias por responder, ahora está trabajando de alguna manera , pero me estoy poniendo

    Line (23) : Error , Unrecognized type ‘out_CamRot’

    Line 23 -> out_CamRot rotator out, out float out_FOV

    tal vez es porque im usando la versión más reciente de UDK ?

    • lo tengo a trabajar de nuevo de alguna manera , pero ahora esta x , x

      Line (29) : Error, bad or missing expression for token: ==, in ‘If’

    • de esta línea

      Line (29)
      if (== WeaponType PWT_AimRight) // Offset varies stance (weapon)

  20. hola marcos ante todo agradecerte que si no fuera por usted no habría avanzado tanto, tengo una pregunta tal vez puedas ayudarme… ¿como podría ponerle sonidos a mi personaje tanto komo a los enemigos? espero me respondas y saludos…

    • Hola Karen, muchas gracias por visitar mi blog!
      Poner sonidos es sencillo. Usando código basta con declarar primero el sonido que quieras usar:

      var SoundCue SwingSound;

      Luego en la parte de defaultproperties le das el valor correspondiente al fichero de sonido que has importado en UDK, por ejemplo:

      SwingSound=SoundCue’A_Vehicle_Scorpion.SoundCues.A_Vehicle_Scorpion_BladeExtend’

      Y finalmente en el resto del código haces que suene con:

      PlaySound(SwingSound, true);

      Espero que te sirva, un saludo!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s