- Home /
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);
}
}
}
}
Answer by aldonaletto · Apr 10, 2012 at 12:01 PM
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.
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?
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 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 } } ...
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.