x


Raycast collided object not transforming when scripted to?

In this meleeAttack code I scripted, the attacked object is gathered via raycast. Everything works fine, the problem lies in that the enemy object wont be transformed by the transform command.

IE: hit.collider.gameObject.transform.Translate(Time.deltaTime*2, Time.deltaTime*2, 0, Space.World);

meleeAttack.js

var cooldownTimer: int = 10;
var countDown : int = 0;
var playerAttackRange : int = 5;
var particle : GameObject;
var player : GameObject;
animation["swordSlash"].layer = 1;
var particleClone = particle;

function Start () {

}

function Update () {



    if(Input.GetButton("Fire1") &&  Time.time > countDown){

       attack();
       }

}

function attack(){
    countDown = Time.time + cooldownTimer;
    if(AbilitySystem.fireElement.equippedFire == true) {

    var hit : RaycastHit;
    var fwd = transform.TransformDirection(Vector3.forward);

    if(Physics.Raycast(transform.position, fwd, hit, playerAttackRange)){
         Debug.Log("Melee has struck " + hit.collider.gameObject + " at a range of " + playerAttackRange +"!!!!!!!!!!!");
         if(hit.collider.gameObject.tag == "Enemy") {
          hit.collider.gameObject.GetComponent(EnemyHealth).enemyCurrentHealth -= AbilitySystem.fireElement.swordDamage;
          particleClone = Instantiate(particle, hit.collider.gameObject.transform.position, hit.collider.gameObject.transform.rotation);
          Destroy(particleClone, 1);
          hit.collider.gameObject.transform.Translate(Time.deltaTime*2, Time.deltaTime*2, 0, Space.World);
          }
         }
       }
    }
more ▼

asked Apr 10 '12 at 02:55 AM

Dreoh gravatar image

Dreoh
49 6 10 12

(comments are locked)
10|3000 characters needed characters left

1 answer: sort voted first

Your problem is caused by the amount you translate:

  ...transform.Translate(Time.deltaTime*2, Time.deltaTime*2, 0, Space.World);

Unless you call Physics.Raycast each Update while the button is pressed (what the cool down timer disallows), the effect will be too small (less than 0.1 unit at 30 frame per seconds). Furthermore, this will always move the target in the same world direction, no matter the direction the weapon strikes it.
A good solution is to apply the movement during a specific time (0.5 seconds, for instance) and yield while this time is being counted (the function attack() is converted automatically to a coroutine by the JS compiler). You can also define the "impact" direction opposite to hit.normal, thus the ray will seem to push the target back:

function attack(){
  if(AbilitySystem.fireElement.equippedFire == true) {
    countDown = Time.time + cooldownTimer;
    var hit : RaycastHit;
    var fwd = transform.forward; // <- easier way to get the forward direction
    if(Physics.Raycast(transform.position, fwd, hit, playerAttackRange)){
      Debug.Log("Melee has struck " + hit.collider.name + " at a range of " + playerAttackRange +"!!!!!!!!!!!");
      if(hit.collider.tag == "Enemy") {
        hit.collider.GetComponent(EnemyHealth).enemyCurrentHealth -= AbilitySystem.fireElement.swordDamage;
        particleClone = Instantiate(particle, hit.transform.position, hit.transform.rotation);
        Destroy(particleClone, 1);
        // calculate the direction to push the target:
        var impact = -hit.normal;
        impact.y = 0; // keep it horizontal
        impact = impact.normalized * 2.0; 
        // apply the movement during duration seconds:
        var duration = 0.5;
        while (duration > 0){
          hit.transform.Translate(dir * Time.deltaTime, Space.World);
          duration -= Time.deltaTime; // count time
          yield; // return to loop next frame
        }
      }
    }
  }
}

NOTE: Notice that I simplified a lot the access to properties like transform, name, tag etc. Most game object properties allow access to the other properties directly: collider.tag, collider.name, transform.gameObject etc.

more ▼

answered Apr 10 '12 at 12:01 PM

aldonaletto gravatar image

aldonaletto
41.3k 16 42 195

Oh wow, you answered my question and then some!

I actually "fixed" this problem before the mods accepted the question, using rigidbody.AddForce, but I noticed that that is not exactly reliable since it seems to push back different distances sometimes.

Your answer seems much, much more efficient and better, and you explained it in such a great way, I would upvote this 10 times if I could lol.

Alright, another quick question. How would I go about adding a kind of "inactive" period for the hit object? What I mean is when the enemy gets hit, they have a recovery time before they can react or act in any way. Would I just have to script the enemy to "yield" if hit, or script them to kind of "pause", and play a "tookDamage" animation?

Edit: 1 more quick question, and I will be done (I swear!). Is there any reason why my enemy won't be translated or "addForce"d away if they are colliding (running into) my player when I attack them with my spell that fires a rock that pushes back or with this melee attack?

Apr 10 '12 at 01:33 PM Dreoh

I would add a recoveryTimer variable to the EnemyHealth script, and refuse to hit it when this timer is > 0:

// in the enemy script:

var recoveryTimer: float = 0; // declare the timer

function Update(){
  // decrement timer if > 0
  if (recoveryTimer > 0) recoveryTimer -= Time.deltaTime;
  ...

// in the player script:
      ...
      if(hit.collider.tag == "Enemy"){
        // get the EnemyHealth script:
        var enemyH: EnemyHealth = hit.collider.GetComponent(EnemyHealth);
        // only hit the enemy if recoveryTimer ended
        if (enemyH.recoveryTimer  0){
            hit.transform.Translate(dir * Time.deltaTime, Space.World);
            duration -= Time.deltaTime; // count time
            yield; // return to loop next frame
          }
        }
        ...
Apr 10 '12 at 01:53 PM aldonaletto

It should work fine, unless you're using hit.normal to calculate the direction (like I did) and at the hit.point the surface has some weird curvature (hit.normal may become too vertical). You could try to replace hit.normal by the vector (enemy)->(ray origin):

  var impact = hit.transform.position - transform.position;

If it still doesn't work, use the player position:

  var impact = hit.transform.position - player.position;

where player is a Transform variable to which you drag the player object.

Apr 10 '12 at 02:03 PM aldonaletto

That actually worked perfectly thank you!

Apr 11 '12 at 09:01 PM Dreoh
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x5058
x3446
x1525
x1275
x71

asked: Apr 10 '12 at 02:55 AM

Seen: 718 times

Last Updated: Apr 11 '12 at 09:01 PM