PFM: Combate cuerpo a cuerpo en UDK (y parte 2)

En la primera parte vimos cómo crear un arma cuerpo a cuerpo y programarla para ejecutar animaciones de melee al hacer clic con el botón de ataque. Sin embargo el arma era bastante inútil, pues no tenía ningún efecto sobre los enemigos. Del cálculo de colisiones con los enemigos es de lo que nos encargaremos hoy.

Sistema de detección de impactos

Existen diferentes métodos para detectar si la espada colisiona con los enemigos. Uno de los más comunes es trazar en cada frame un rayo desde la base de la espada a la punta, de modo que si hay algun objeto que sea interceptado por el rayo significa que la espada está en contacto con el mismo. El problema de este sistema es que no es 100% fiable, ya que es posible que en un fotograma la espada esté a punto de contactar con el objeto y en el siguiente fotograma la espada ya haya atravesado dicho objeto, resultando en que no se detecte la colisión.

Por ello el método que vamos a seguir es diferente. Trazaremos rayos desde la posición actual de la punta de la espada a su posición en el fotograma anterior. De este modo se detecta cualquier colisión que haya podido producirse entre frames diferentes. Lo mismo haremos con la base de la espada, teniendo 2 puntos de comprobación. Pero, ¿y si el objeto se encuentra entre la punta y la base de la espada? No se detectaría la colisión… Por ello lo que haremos es dividir toda la longitud de la espada en varios puntos, realizando la comprobación de colisión en cada uno de ellos. Para que se entienda, la siguiente imagen muestra con líneas verdes las comprobaciones de colisión que se han realizado a lo largo del movimiento de ataque.

meleePara conocer la posición de la base y la punta de la espada en cada fotograma usaremos los sockets que cremos en la parte 1. Veamos el código necesario.

PFMWeapon

En primer lugar añadimos la siguiente función a PFMWeapon, que será útil para conocer la posición global de los sockets.

function vector GetSocketLocation(Name SocketName, SkeletalMeshComponent SMC)
{
    local SkeletalMeshSocket SMS;

    if (SMC != none)
    {
        SMS = SMC.GetSocketByName(SocketName);
        if (SMS != none)
        {
            return SMC.GetBoneLocation(SMS.BoneName);
        }
    }
}

PFMWeaponBlade

El resto de modificaciones se realizan en la clase que creamos para la espada en la primera parte. Primero añadiremos nuevas variables. Los puntos de la espada se guardarán en 2 arrays diferentes, uno para sus posiciones actuales y otro para sus posiciones anteriores.

// Cálculo de impactos
var array<Vector> PreviousPoints;       // Puntos de la espada en el frame anterior
var array<Vector> CurrentPoints;        // Puntos de la espada en el frame actual
var int numPoints;                      // Número de puntos totales en que se divide la espada
var Name baseSocketName;                // Nombre del socket de la base
var Name tipSocketName;                 // Nombre del socket de la punta
var array<Actor> HitActors;             // Array de actores golpeados

var ParticleSystem HitEffect;           // Partículas para efecto de impacto
var SoundCue HitSound;                  // Sonido de impacto

Extendemos el estado WeaponFiring para sobreescribir la funcionalidad de Tick, de modo que llame a nuestra función de traceado cuando se realice el ataque.

// Se extiende el estado para detectar las colisiones
// El estado WeaponFiring dura hasta llamar a RefireCheckTimer y que determine que se ha terminado el ataque
state WeaponFiring
{
    simulated event Tick(float DeltaTime)
    {
        super.Tick(DeltaTime);
        TraceSwing();
    }
}

Usaremos la siguiente función para añadir al array de actores golpeados por el arma aquellos que no estén ya en dicho array. De este modo llevaremos la cuenta de los actores golpeados en cada ataque, evitando que se hiera dos veces al mismo actor en un sólo ataque.

function bool AddToHitActors(Actor HitActor)
{
    // Se hace una búsqueda previa para ver si ya estaba introducido
    if(HitActors.Find(HitActor) != -1)
    {
        return false;   //Ya estaba, no se introduce
    }

    HitActors.AddItem(HitActor);
    return true;
}

Esta es la función que calcula las colisiones entre un frame y el siguiente. Tras obtener la posición actual de los sockets, se actualizan las posiciones de los puntos de control a lo largo de la espada. Luego se calculan los impactos y se aplica el daño correspondiente a los actor que se hayan impactado.

function TraceSwing()
{
    local Vector BaseSocketLocation, TipSocketLocation;
    local int i;
    local Actor HitActor;
    local Vector HitLocation, HitNormal, Momentum;

    BaseSocketLocation = GetSocketLocation(baseSocketName, SkeletalMeshComponent(Mesh));
    TipSocketLocation = GetSocketLocation(tipSocketName, SkeletalMeshComponent(Mesh));

    // Actualización de posiciones
    for(i = 0; i < numPoints; i++)
    {
        CurrentPoints[i] = VLerp(BaseSocketLocation,TipSocketLocation,i/float(numPoints-1));
    }

    // Cálculo de impactos
    for(i = 0; i < numPoints; i++)
    {
        // Se recorren los actores interceptados por el traceado
        foreach TraceActors(class'Actor', HitActor, HitLocation, HitNormal, PreviousPoints[i], CurrentPoints[i])
        {
            // Si no se había dañado a este actor en el ataque actual
            if(HitActor != Instigator && AddToHitActors(HitActor))
            {
                HitActor.TakeDamage(60, Instigator.Controller, HitLocation, Momentum, class'DamageType');
                SpawnHitEffects(HitLocation,HitNormal);
            }
        }
    }

    // Debug lines
    /*for(i = 0; i < numPoints; i++)
    {
        DrawDebugLine(PreviousPoints[i],CurrentPoints[i],0,255,0,true);
    }*/

    // Actualización de puntos anteriories
    for(i = 0; i < numPoints; i++)
    {
        PreviousPoints[i] = CurrentPoints[i];
    }
}

ResetTraceSwing prepara el sistema de impactos antes de cada ataque, por lo que hay que invocar esta función al comienzo de dicho ataque.

// Prepara el sistema para tracear
function ResetTraceSwing()
{
    local Vector BaseSocketLocation, TipSocketLocation;
    local int i;

    BaseSocketLocation = GetSocketLocation(baseSocketName, SkeletalMeshComponent(Mesh));
    TipSocketLocation = GetSocketLocation(tipSocketName, SkeletalMeshComponent(Mesh));

    // Reseteo de los arrays
    PreviousPoints.Remove(0,PreviousPoints.length);
    CurrentPoints.Remove(0,PreviousPoints.length);
    HitActors.Remove(0,HitActors.length);

    // Valores iniciales
    for(i = 0; i < numPoints; i++)
    {
        PreviousPoints.AddItem(VLerp(BaseSocketLocation,TipSocketLocation,i/float(numPoints-1)));
        CurrentPoints.AddItem(VLerp(BaseSocketLocation,TipSocketLocation,i/float(numPoints-1)));
    }
}

Para llamar a esta función modificamos FireAmmunition, añadiéndole una llamada a ResetTraceSwing.

// Disparo efectivo
simulated function FireAmmunition()
{
    // Se declara que el ataque se ha cumplido
    Super.StopFire(CurrentFireMode);

    // Resetea el sistema de impactos
    ResetTraceSwing();

    // ....etc, el resto es igual

Esta función se encarga de hacer aparecer las partículas y sonidos derivados del impacto.

// Efectos de impacto
function SpawnHitEffects(vector HitLocation, vector HitNormal)
{
    // Particulas de explosión
    if(HitEffect != None)
    {
        WorldInfo.MyEmitterPool.SpawnEmitter(HitEffect, HitLocation, rotator(HitNormal));
    }

    // Sonido de impacto
    if (HitSound != None)
	{
		PlaySound(HitSound, true);
	}
}

En la sección de defaultproperties, habrá que dar valor a las variables pertinentes, incluyendo el nombre de los sockets que hayamos decidido en el Socket Manager.

numPoints=5
    baseSocketName="Base"
    tipSocketName="Tip"

    HitEffect=ParticleSystem'VH_Cicada.Effects.P_VH_Cicada_Decoy_Explo'
    HitSound=SoundCue'A_Character_BodyImpacts.BodyImpacts.A_Character_RobotImpact_GibMedium_Cue'

Y ya está.

Clang! Clang!

Comprobemos qué tal impacta nuestra espada con los cansinos Razor

A continuación el código completo de PFMWeaponBlade

class PFMWeaponBlade extends PFMWeapon;

var array<Name> SwingAnimations;         // Nombres de las animaciones a usar
var array<float> SwingIntervals;         // Intervalos de tiempo de cada animación
var int currentSwing;                    // Animación actual

// Cálculo de impactos
var array<Vector> PreviousPoints;       // Puntos de la espada en el frame anterior
var array<Vector> CurrentPoints;        // Puntos de la espada en el frame actual
var int numPoints;                      // Número de puntos totales en que se divide la espada
var Name baseSocketName;                // Nombre del socket de la base
var Name tipSocketName;                 // Nombre del socket de la punta
var array<Actor> HitActors;             // Array de actores golpeados

var ParticleSystem HitEffect;           // Partículas para efecto de impacto
var SoundCue HitSound;                  // Sonido de impacto

var SoundCue SwingSound;                // Sonido de espadazo

// Se sobreescribe para evitar que al soltar el botón se desactive cualquier ataque
simulated function StopFire(byte FireModeNum);

// Se extiende el estado para detectar las colisiones
// El estado WeaponFiring dura hasta llamar a RefireCheckTimer y que determine que se ha terminado el ataque
state WeaponFiring
{
    simulated event Tick(float DeltaTime)
    {
        super.Tick(DeltaTime);
        TraceSwing();
    }
}

// Disparo efectivo
simulated function FireAmmunition()
{
    // Se declara que el ataque se ha cumplido
    Super.StopFire(CurrentFireMode);

    // Resetea el sistema de impactos
    ResetTraceSwing();

    // Se elimina posible Timer de reseteo del sistema de melee
    ClearTimer(nameof(ResetMelee));

    // Animación de melee (AnimName, Rate, BlendInTime, BlendOutTime, bLooping, bOverride)
    PFMPawn(Owner).MeleeAnimNode.PlayCustomAnim(SwingAnimations[currentSwing], 1.0,0.05,0.1,false,true);

    // Efectos
    SpawnSwingEffects();

    // Se reprograma el intervalo de RefireCheckTimer adecuadamente
    FireInterval[0] = SwingIntervals[currentSwing];
    TimeWeaponFiring(CurrentFireMode);

    // Aumenta el índice de las animaciones para pasar a la siguiente
    currentSwing = (currentSwing + 1) % SwingAnimations.length;

    // Se desactiva el desplazamiento del Pawn
    PFMPlayerController(Pawn(Owner).Controller).SetRotationOnly(true);

    Super.FireAmmunition();
}

//***********************
//  IMPACTOS
//***********************
function bool AddToHitActors(Actor HitActor)
{
    // Se hace una búsqueda previa para ver si ya estaba introducido
    if(HitActors.Find(HitActor) != -1)
    {
        return false;   //Ya estaba, no se introduce
    }

    HitActors.AddItem(HitActor);
    return true;
}

function TraceSwing()
{
    local Vector BaseSocketLocation, TipSocketLocation;
    local int i;
    local Actor HitActor;
    local Vector HitLocation, HitNormal, Momentum;

    BaseSocketLocation = GetSocketLocation(baseSocketName, SkeletalMeshComponent(Mesh));
    TipSocketLocation = GetSocketLocation(tipSocketName, SkeletalMeshComponent(Mesh));

    // Actualización de posiciones
    for(i = 0; i < numPoints; i++)
    {
        CurrentPoints[i] = VLerp(BaseSocketLocation,TipSocketLocation,i/float(numPoints-1));
    }

    // Cálculo de impactos
    for(i = 0; i < numPoints; i++)
    {
        // Se recorren los actores interceptados por el traceado
        foreach TraceActors(class'Actor', HitActor, HitLocation, HitNormal, PreviousPoints[i], CurrentPoints[i])
        {
            // Si no se había dañado a este actor en el ataque actual
            if(HitActor != Instigator && AddToHitActors(HitActor))
            {
                HitActor.TakeDamage(60, Instigator.Controller, HitLocation, Momentum, class'DamageType');
                SpawnHitEffects(HitLocation,HitNormal);
            }
        }
    }

    // Debug lines
    /*for(i = 0; i < numPoints; i++)
    {
        DrawDebugLine(PreviousPoints[i],CurrentPoints[i],0,255,0,true);
    }*/

    // Actualización de puntos anteriories
    for(i = 0; i < numPoints; i++)
    {
        PreviousPoints[i] = CurrentPoints[i];
    }
}

// Prepara el sistema para tracear
function ResetTraceSwing()
{
    local Vector BaseSocketLocation, TipSocketLocation;
    local int i;

    BaseSocketLocation = GetSocketLocation(baseSocketName, SkeletalMeshComponent(Mesh));
    TipSocketLocation = GetSocketLocation(tipSocketName, SkeletalMeshComponent(Mesh));

    // Reseteo de los arrays
    PreviousPoints.Remove(0,PreviousPoints.length);
    CurrentPoints.Remove(0,PreviousPoints.length);
    HitActors.Remove(0,HitActors.length);

    // Valores iniciales
    for(i = 0; i < numPoints; i++)
    {
        PreviousPoints.AddItem(VLerp(BaseSocketLocation,TipSocketLocation,i/float(numPoints-1)));
        CurrentPoints.AddItem(VLerp(BaseSocketLocation,TipSocketLocation,i/float(numPoints-1)));
    }
}

//*****************************
//  GESTIÓN ENTRE DISPAROS
//*****************************

// Programa un timer para llamar a RefireCheckTimer
simulated function TimeWeaponFiring( byte FireModeNum )
{
    // Si desde que se inició el ataque anterior se ha pulsado otra vez clic, RefireCheckTimer volverá a
    // lanzar un ataque, pues PendingFire será = 1
    SetTimer( GetFireInterval(FireModeNum), false, nameof(RefireCheckTimer) );
}

// Llamado al desactivar este arma
simulated function PutDownWeapon()
{
    HandleFinishedFiring();
    Super.PutDownWeapon();
}

// Llamado cuando se deja de "disparar". Se llama si RefireCheckTimer determina que no hay que seguir atacando.
simulated function HandleFinishedFiring()
{
    if (PFMPawn(Owner).MeleeAnimNode.GetCustomAnimNodeSeq() != None)
    {   // Se activa un Timer que reseteará el sistema de Melee cuando termine la animación actual
        SetTimer(PFMPawn(Owner).MeleeAnimNode.GetCustomAnimNodeSeq().GetTimeLeft(), false, nameof(ResetMelee));
    }

    // Se reactiva el movimiento del Pawn
    PFMPlayerController(Pawn(Owner).Controller).SetRotationOnly(false);

    Super.HandleFinishedFiring();
}

// Se llama cuando la animación de melee concluye. Resetea el sistema para que las animaciones comiencen desde la primera.
function ResetMelee()
{
    // Se resetea el combo
    currentSwing = 0;
}

//********************************
//  EFECTOS VISUALES Y DE SONIDO
//********************************

// Efectos de ataque
function SpawnSwingEffects()
{
    // Sonido de impacto
    if (SwingSound != None)
	{
		PlaySound(SwingSound, true);
	}
}

// Efectos de impacto
function SpawnHitEffects(vector HitLocation, vector HitNormal)
{
    // Particulas de explosión
    if(HitEffect != None)
    {
        WorldInfo.MyEmitterPool.SpawnEmitter(HitEffect, HitLocation, rotator(HitNormal));
    }

    // Sonido de impacto
    if (HitSound != None)
	{
		PlaySound(HitSound, true);
	}
}

defaultproperties
{
    // Mesh
    Begin Object Class=SkeletalMeshComponent Name=GunMesh
        SkeletalMesh=SkeletalMesh'Weapons.PepeDummyWeaponBlade.SM_PepeDummyWeaponBlade'
        HiddenEditor=FALSE
        Scale=1.0
    End Object
    Mesh=GunMesh
    Components.Add(GunMesh)

    bMeleeWeapon=true;
    bInstantHit=true;
    bCanThrow=false;

    FiringStatesArray(0)=WeaponFiring
    WeaponFireTypes(0)=EWFT_Custom

    FireInterval(0)=0.0                     // Intervalo entre disparos
    currentSwing=0                          // Animación actual
    SwingAnimations=("PepeDummy_Swing1","PepeDummy_Swing2") //Animaciones diferentes
    SwingIntervals=(0.4,0.35)                                //Intervalos de cada animación

    numPoints=5
    baseSocketName="Base"
    tipSocketName="Tip"

    HitEffect=ParticleSystem'VH_Cicada.Effects.P_VH_Cicada_Decoy_Explo'
    HitSound=SoundCue'A_Character_BodyImpacts.BodyImpacts.A_Character_RobotImpact_GibMedium_Cue'

    SwingSound=SoundCue'Weapons.PepeDummyWeaponBlade.SC_Sword_Swing'

    // Grupo de inventario
    InventoryGroup=1

    // Tipo de postura
    WeaponType=PWT_Default
}

Banner Blog

Anuncios

33 pensamientos en “PFM: Combate cuerpo a cuerpo en UDK (y parte 2)

  1. Pingback: PFM: Combate cuerpo a cuerpo en UDK (parte 1) | El Blog de Marcos

  2. Pingback: PFM: Diseño y creación de enemigo Razor (y parte 2) | El Blog de Marcos

  3. Hola Marcos,
    estoy acatascado con esto, he llegado bien hasta el final de la primera parte del cuerpo a cuerpo, meter la animacion, los sonidos del movimiento y del impacto.
    Pero no consigui realizar el ataque con el trace por que me da el siguiente error

    Error 5 Bad or missing expression for token: GetSocketLocation, in ‘=’

    Los nombres de los sockest estan puestos bien (copiar-pegar para no equivocarnos)
    y la verdad es que no se que le puede picar a esa funcion. El intelisene no la reconoce si no la llamo desde mi pawn por ejemplo, pero buscamos los sockets del arma, y de todas formas me sigue dando el mismo error, pero en vez de en GetSocket… en Bio……
    ¿Se te ocurre algun por que?

    • Hola Guille, creo que el problema es que falta en el código la siguiente función:

      function vector GetSocketLocation(Name SocketName, SkeletalMeshComponent SMC)
      {
      local SkeletalMeshSocket SMS;

      if (SMC != none)
      {
      SMS = SMC.GetSocketByName(SocketName);
      if (SMS != none)
      {
      return SMC.GetBoneLocation(SMS.BoneName);
      }
      }
      }

      Yo la tengo puesta en PFMWeapon, pero creo que no actualicé el código del tutorial y por eso te da error con que no la encuentra…

    • “No hay nada como ir al medico como para que se te quiten todos lo males” XD
      Pues creo que he dado con la clave, he modificado la forma de usar el metodo getsocketlocation que usas por lo siguiente:

      SkeletalMeshComponent(Mesh).GetSocketWorldLocationAndRotation(baseSocketName, BaseSocketLocation);

      Y compila, pero no veo los traces ni poniendolo en slomo (si, las lineas estan descomentadas 😉 )

    • Si, si que esta, pero como te digo, en la forma que tu la llamas no me chusta, no se por que.

      Bueno lo de los traces ya esta arreglado, es que les habia cambiado el nombre en el ediitor y no habia actualizado…
      Otra duda, que esta relacionada con lo que hablais cobalt y tu de que el trac se hace solo en la primera parte de la animacion.
      nosotros no tenemos hecha la clase weapon como tu, ya que antes era una instanthit normal, como una pistola, y la estamos modificando poco a poco.
      Entonces no estamos usando los arrays de las animaciones ni de sus tiempos, asi que modificando el fireInterval, alargo la duracion del trace y me crea las estelas durante toda la animacion, ¿pero se puede decir al trace que empiece algun tiempo despues? para salvar la parte preparacion del movimiento

  4. Bueno, dejando aparte la pijada de que el trace no se haga durante toda la animacion, tengo el inconveniente de que el arma sigue siendo de tipo instanthit, no se por que, ya que hace todo lo demas (detecta los impactos, repoduce bien el sonido, creo que hasta pone decals, pero no del todo bien), pero si le haces un ataque lejos del objetivo en linea recta, sin contacto, muere.

    • Bueno esta tambien esta ya listo :p
      Le he cambiado estas dos propiedades

      bInstantHit = false
      WeaponFireTypes(0) = EWFT_Custom

      y va al pelo, menos mal por que tengo que hacer un par de armas mas de este estilo.
      Si no fuera por tu blog estaria tirandome de lo pelos Marcos, muchas gracias 😉

  5. Hola,
    ¿Se nota que tenemos que entregar el domigo como muy tarde? XD

    Pues enredando e intentando aplicar los traces al combate con los puños, los dos en una sola arma, y cada puño es un ataque diferente del mismo arma, he encontrado la forma de que el personaje ataque con el arma de melee y ademas se pueda mover de forma natural.
    El temario esta en usar otro tipo de AnimNode accesible desde codigo y este es el AnimNodePlayCustomAnim, que este si te deja enchufarle un AnimNodeSequence en la rama alternativa. El que estabamos usando no lo permitia y eso me chirriaba un poco.

    El AnimTree queda tal que asi

    Se ha añadido un AnimBlendPerBone antes de todo para que interpole las demas animaciones con los ataques, y escribiendo esto acabo de darme cuenta de que se puede hacer tambien con el NodeSlot que se usaba antes, la clave esta en este BlendPerBone. Su rama principal sigue por donde antes y no se ha modificado nada, y en el target se coloca el nodo accesible por codigo que se desee.
    Por codigo lo hay que cambiar del pawn la clase de la variable que apunta al nodo “especial”, que sera del tipo AnimNodePlayCustomAnim.
    Como he comentado no haria falta modifical el tipo del nodo ni el tipo dela variable. Ademas al nodo AnimNodePlayCustomAnim del AnimTree solo se le pueden enchufar NodeSequence por el camino alternativo, ya que no puede aplicarle el valor de la secuencia de animacion que queremos reproducir a otro tipo de nodo, asi que a fin de cuenta se comporta igual que el otro, pero permite dejar todas las ramas del arbol llenas de hojas, si se quiere…

    • Claro en mi caso el BlendPerBone sólo lo he aplicado a las armas de fuego y por eso al final del árbol le meto el AnimNodeSlot para pinchar las animaciones de Melee cuando sean necesarias. Lo de los AnimTree es toda una ciencia 😆
      Ánimo y pon por aquí el resultado.

    • lo que no se por que hace el play del sonido del impacto del puñetazo, bueno si lo se, creo que esta impactando con el player. Intente añadir la condicion de que si el hitactor es el pawn del jugado no la añadiera a lista de impactados, pero no va y ademas creo que eso ya lo tienes tu controlado, asi que no se por que sera.

    • Tiene bastante buena pinta! Si suena es que algo está impactando, prueba a hacer un log a ver si averiguas qué puede ser

    • Le hice un log para ver el nombre del pawn con el que impacta y era BioPawn_0 que supongo que es el del jugador, por eso digo que sera que impacta con el mismo, pero no entiendo por que.
      El arma es un cubo con dos sockets (creo que la cosa puede estar ahi), estan bastante juntos por que es un cubo muy pequeño estan tan jusntos que hasta es tocan, y dependiendo del currentFireMode le hago un AttachWeaponto al socket de la mano que ataca.
      La verdad que no he probado a separar los sockets del arma a ver si es por que se estan tocando y lo que esta reportando es un impacto consigo mismo (del arma con el arma).
      Luego lo pruebo.

    • Si, una manera sería adaptar este sistema pero usando los sockets creados en los puños del Pawn. La programación iría en el propio Pawn y habría que hacer algunas modificaciones pero sí, es posible 😀

    • Nosotros lo hicimos igual que si fuera una espada, no caimos en la cuenta de hacerlo con los sockets del personaje.
      Lo que hicimos fue hacer un cubo muy pequeño, importarlo como armaPuño y colocarlo dentro de la mano del personaje para que no se vea, el resto es exactamente igual, bueno, en vez de hacer 5 divisiones hicimos solo 2, si se toca un poco mas el codigo se puede hacer sin divisiones o hacer una sola iteracion el el bucle.
      Funcionaba al pelo.

  6. Disculpa marcos noce si me podrias ayudar con una pequeña inquietud que tengo aqui en que parte defino el daño q hace la espada para disminuirlo o incrementarlo..
    Muchas gracias por tu atencion y felicidades por el lanzamiento de tu juego me encanto 🙂 tambien me gustaria ver mas tutoriales que no se terminen pero bueno gracias por todo

    • Hola Andres, para modificar el daño que produce la espada puedes cambiar la línea:

      HitActor.TakeDamage(60, Instigator.Controller, HitLocation, Momentum, class’DamageType’);

      El primer valor después de TakeDamage es el daño inflingido sobre el actor colisionado, que debería estar en una variable por cierto xD
      Me alegro que te guste el juego y espero poder seguir haciendo tutoriales próximamente 😀

    • Gracias por la respuesta marcos eres el mejor 😀 y si algún rato as algún otro estaré ansioso esperando 😀

  7. Hola Marcos disculpa tengo una inquietud y si me ayudas te lo agradeceria mucho la verdad quiero que me ayudes como le daria impulso al enemigo e tratado con mesh.AddImpulse pero no va veo que con las demas armas los enemigos reaccion y tienen impulso por ser EWFT_InstanHit pero el cuerpo a cuerpo es EWFT_Custom como le ago para que tenga impulso el enemigo cuando lo golpeo?

    gracias de antemano marcos 🙂

    • Hola Andres, una función que puedes usar es TakeDamage de la clase Actor:

      event TakeDamage(int DamageAmount, Controller EventInstigator, vector HitLocation, vector Momentum, class DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)

      Entre otras cosas puedes especificar un Momentum que determina el impulso debido al daño.

    • Muchas gracias marcos seguro funciona enserio gracias 🙂 por responder eres grande XD

    • Podrias darme un ejemplo de como agregrar impulso porque la verdad ando un poco perdido en la funcion TraceSwing() realizamos el daño que seria

      HitActor.TakeDamage(DamageBaston, Instigator.Controller, HitLocation, Momentum, class’DamageType’);

      pero mi pregunta es como le doy un impulso al enemigo? como especifico ese momentum si me das un ejemplo te lo agradeceria

    • Bueno lo e logrado Gracias Igual Marcos dejo como lo hice por si a alguien le sirve ..

      if(HitActor != Instigator && AddToHitActors(HitActor))
      {
      //impulse y impulso_baston son float la una es local y la otra se accede desde el defaultpropeties

      Impulse=impulso_baston;

      Momentum = Normal(HitLocation – AMPawn(Owner).Location) * Impulse;

      WorldInfo.Game.Broadcast(self,Momentum);

      HitActor.TakeDamage(DamageBaston, Instigator.Controller, HitLocation,Momentum, class’DmgTypeBaston’);

      SpawnHitEffects(HitLocation,HitNormal);
      }

  8. Hola Marcos, primero que nada decirte que gracias a estos tutoriales he aprendido un montón de cosas nuevas y de nuevas variables. Ai algo que me esta dejando sin cerebro y bueno he decidido preguntartelo. Como puedo hacer para cuando aga la Animacion de atakar en vez de que siga corriendo se pare aga la animacion y luego corra.

    Gracias por tu atencion

    • Hola Coywen, en este mismo tutorial se aplica ese comportamiento que describes. Como se ve en el video, el personaje se para al realizar el ataque.

  9. Hola Marcos Perdon por molestarte se que esta es una pregunta algo extraña pero me preguntaba si tendrias algun tutorial sobre un Arma que funcione con este sistema pero en Unity en JavaScript me ayudarias muchisimo , bueno tambien quiero agradecerte por este tutorial me a servido muchisimo.

    • Lo logre Marcos perdon por la pregunta todo es transcrivible con algunos cambios igual gracias Marcos tus tutoriales son geniales son de lo mejor.

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