I have a terrain with two enemies that I placed and a plane that I have given a script to spawn enemies continuously (basically wave defence without towers)
The two enemies I placed work as intended, killing them will give experience and coming into contact with them cost the player some health.
But the ones spawned by my scripted plane don't grant experience on a kill, the only reason they do health damage on contact is because I set the collision to look for a tag of "Enemy" which they have.
# THE SCRIPT FOR THE ENEMY-SPAWNING PLANE
IEnumerator Spawner()
{
yield return new WaitForSeconds(startWait);
while(!stop)
{
randEnemy = Random.Range(0, 2);
Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), 2, Random.Range(-spawnValues.z, spawnValues.z));
Instantiate(enemies[randEnemy], spawnPosition + transform.TransformPoint(0, 0, 0), gameObject.transform.rotation);
yield return new WaitForSeconds(spawnWait);
}
}
# THE SCRIPT WHICH CALLS FOR EXPERIENCE TO BE GIVEN (Enemy as GameObject)
void Dead()
{
_characterXP.GainExp(120);
Destroy(gameObject);
}
# THE SCRIPT WHICH GIVES THE PLAYER EXPERIENCE (CharacterXP as GameObject)
public void GainExp(float expThatWasGained)
{
CurrentExp += expThatWasGained;
}
I'm still pretty new to Unity and C# so if there is anything else that's needed I can provide it, I have checked all relevant objects to make sure the relevant things are connected correctly, and if they didn't work then it would affect the original enemies, the issue is only in regards to the spawned enemies that get named ENEMY(Clone)
EDIT
The Prefab ^
The Pre-Spawned Enemy Mid-Game^
The Clone Enemy Mid-Game^
You cannot assign scene objects to prefab property fields. You can only assign other prefab (or assets) into the prefab property slots.
To assign a scene object to your enemy script, you need to find it at runtime.
In your enemy's Start method, find the fpscontroller
void Start()
{
_characterXP = GameObject.FindObjectOfType<FPSController>();
}
I think this will solve your problem
Not the most intelligent way of doing this, but I was on a tight schedule and the method works.
I created two prisons outside of the playable area and put one of each enemy inside of it and then referred to these two trapped individuals in my Monster Spawner (The Plane) instead of the prefabs and now I gain the right amount of exp per kill.
Related
I'm making a game in unity for a school project. I'm stuck on this one part where I want a block to fall and be destroyed once a player has touched the block and moved to the next. Having so much trouble and would love some assistance.
The concept of what i'm aiming for can be seen here: http://www.coolmath-games.com/0-b-cubed
On each block, you'll need to attach a script which contains the OnCollisionExit () method (reference). Pass in the collision parameter (see reference) and use the collision info to confirm that the object leaving the collider is the player (eg tag the player with a player tag in the inspector and check the collision's collider's tag).
In this method, place your code for making the block fall.
Make sure you've added colliders to your objects so that they interact. If you run into problems, post back some code and I'll get back to you.
Actually you do not need to detect collision here. It is not neccessary. Just compare base cube position with player cube position in x,z plane (there should be only difference in Y-axis since player cube is over base cube). No need for collsions here. Than you attach script to all base cubes checking for playercube to hover other them (position check) and than on playercube next movement you attach rigidbody to basecube and destroy it after a second. Simple:)
EDIT
The code should look like this. More or less.
GameObject playerCube; //this is reference to Player object
bool playerEnter = false;
bool playerLeft = false;
void Start()
{
playerCube = Gameoject.Find("PlayerCube"); // here you put the name of your player object as a string. Exactly as it is in the hierarchy
}
void Update()
{
if(playerCube.transform.position.x == transform.position.x && playerCube.transform.position.z == transform.position.z)
{
playerEnter = true; // this checks if player stepped on the cube
}
if((playerCube.transform.position.x != transform.position.x || playerCube.transform.position.z != transform.position.z) && playerEnter == true && playerLeft == false) //checks if player left the cube
{
playerLeft = true; // we do this so the code below is executed only once
gameObject.AddComponent<Rigidbody>(); // ads rigidbody to your basecube
Destroy(gameObject, 1.0f); //destroys baseCube after one second
}
I think that more or less that should do it. In the final game I probably would use coroutines for your task. Also you need to think how do you check if the cube already was destroyed, I mean how will your game detect if player can step on the next cube - does it exist or not?
EDIT 2
Hard to tell what will work or not without your hierarchy, your code and knowing what do you want to have exactly. Study this code - it will work after some adjustments to your needs
I have an issue that I've not been able to solve for a few days now and without it working I can't move on with my project, so your help would be greatly appreciated!
What I'm trying to do is 'find' the velocity of an object that is directly below my 2D sprite (that also contains a Rigidbody and 2D box collider) and then add that velocity (in the same direction) to the object that is 'looking' for it.
I feel like ray-casting might be part of the answer but I'm not sure how to use that, especially in this context.
The idea behind this is I have a platform that can carry objects stacked on top of each other, so you move the mouse, it manipulates the platforms velocity, but this causes the objects on-top to fly backwards, no mater how high the friction is.
Here is the platform code:
void Update()
{
float distance_to_screen = Camera.main.WorldToScreenPoint(gameObject.transform.position).z;
Vector3 pos_move = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance_to_screen));
mouseDelta = pos_move - lastMousePosition;
acceleration = mouseDelta.x * 0.4f;
platformVelocity += acceleration;
platform.velocity = new Vector2(platformVelocity, 0) * speed;
lastMousePosition = pos_move;
}
Thank you for taking the time to read this, I would very much appreciate any help you can give!
Is it necessary to 'find' that object? 'Finding' may be difficult & costing.
Can you hold the 'below-thing' reference and pass it to 'above-thing'?
I'm assuming you have some method on objects that checks whether it's on a moving platform. One option is to add an IsOnMovingPlatform flag on those objects. Then use a similar method to check if any object is on top of the "IsOnMovingPlatform == true" objects. Keep doing passes on that method until you don't come up with any more flags set to true.
So, i'm trying to understand your question and I think I might know what you're talking about.
Are you saying that you want your game object (2d sprite) to stay on the platform as its moving? I have had the same problem, where a platform is moving and my player (game object) slides off- and this is how I fixed it.
Add a box collider to the platform (trigger). When the player enters the platform, make the player's transform the platform. That way, when the platform moves- the player moves with it no matter what the speed is.
For example:
void OnTriggerEnter2D(Collider2d collider){
if(collider.tag.equals("Player")){ // you only want the player to work
// attach script to platform for "this.transform" to work
collider.transform.SetParent(this.transform);
}
}
then, to unparent the transform "otherwise, even when the player is off the platform, it will move with the platform.
void OnTriggerExit(Collider2d collider){
// assuming your player isn't supposed to have a parent!
collider.transform.parent = null;
}
Sorry if I misunderstood your question! If this is what you were going for, finding the velocity of the platform is not necessary. Also, this is uncompiled code- if you have issues let me know.
EDIT:
To add a bit of a give, for the entire duration that the player is in contact with the platform, we can add a little bit of a negative force to shove the player a little bit!
We do this by using OnTriggerStay2D (assuming your game is 2d, otherwise use OnTriggerStay).
For example:
void OnTriggerStay2D(Collider2d collider){
if(collider.tag.equals("Player")){
collider.getComponent<Rigidbody2D>().addForce(new vector2(forceToAdd, 0));
}
}
now, that "forceToAdd" variable should be based on the speed and direction that your platform is going. Does your platform have a rigid body on it? if so you can get your force variable by retrieving the rigid body from the platform (getcomponent()) and get the X velocity from it. With this velocity, experiment with dividing it by numbers until you find a good fit, multiply it by -1 (because you want to push the player in the opposite direction of the platform travel), then this will be your forceToAdd!
However, if your platform is not using a Rigidbody, then a way you can do this is record the x positions in your script (just two positions, and subtract these to find the distance in an update method), multiply by -1, try dividing by a number (experiment with this) and that can be your force to add variable.
Also, my code for the OnTriggerStay2d is rough, the spelling could be a bit wrong- but this will give you a rough idea for a solution! ALSO.. I don't think getting the component of a rigid body should be done in every frame, a way around this would to have your rigid body component as a global variable in the script, then set your rigid body on OnTriggerEnter2d, this way it will only retrieve it once (instead of every frame) which I assume would be more efficient!
Good luck! and if you have any questions il try to help!
This is a raycast2d version, I tested it and it works much better than the trigger collider version :).
using UnityEngine;
using System.Collections;
public class RaycastFeetDeleteLater : MonoBehaviour {
[SerializeField] private float raycastDist = 0.1f;
[SerializeField] Transform feet;
[SerializeField] private float xForceDampener = 0.1f;
private Rigidbody2D rb2d;
// Use this for initialization
void Start () {
rb2d = GetComponent<Rigidbody2D> ();
}
// Update is called once per frame
void FixedUpdate () {
RaycastDown ();
}
private void RaycastDown(){
RaycastHit2D hit = Physics2D.Raycast (feet.position, Vector2.down, raycastDist);
if (hit.collider != null) {
print (hit.transform.tag);
if (hit.collider.tag.Equals ("Platform")) {
transform.SetParent (hit.transform);
Vector2 force = new Vector2 ((hit.transform.gameObject.GetComponent<Rigidbody2D> ().velocity.x * xForceDampener) * -1, 0);
print (force);
rb2d.AddForce (force);
} else if (transform.parent != null) {
print ("UNPARENT");
transform.parent = null;
}
} else {
if (transform.parent != null) {
transform.parent = null;
}
}
Debug.DrawRay (feet.position, Vector3.down, Color.green, raycastDist);
}
}
This script is attached to the player! No need to have scripts on the platforms, which was the main reason my previous method was a pain :P
Basically this script does the following:
- Gets rigidbody2d of the player on start (we will be adding forces to the player with it soon, so might as well store it in a variable!
On FixedUpdate (Different than Update, FixedUpdate is called on Physics update, where as Update is called every frame) I have read that raycast should use FixedUpdate, I suppose because it is a Physics method!
RaycastDown shoots a 2D raycast directly down from the feet position (to get the feet, create a child object of the player, put the position of the feet a tiny bit below the player, NOT ON THE PLAYER- I will explain why in a second why that will mess up everything! Then drag the feet GameObject on to the "feet" slot of the script.)
Raycast shoots from feet directly down at a distance that is changeable in the editor (raycastDist) I found a distance of 0.1f was perfect for my needs, but play around with it for yours! You can actually see a drawing of your Ray by using Debug.DrawRay as seen in the code, the ray is only visible in the editor- not game view! But this will help, especially with positioning the feet.
Checks if it hit an object with a collider, if so check if the objects tag is a Platform (set the platforms to a new tag and call it Platform).
If it hits a platform set the parent of the player to the platform (like the last script) and start applying a little force to the player in the negative direction that the platform is traveling (we get the rigid body of the platform to find its velocity).
If the raycast does not hit a platform, unparent it so the player can move freely.
Thats basically it, however...
Please note:
This script needs to be on the player, not any of the players child objects, since this script will unparent the player from any platforms- if you have the script on a child object of the player it will unparent from the player- which is horrible. So The script MUST be on the root game object of the player. There are ways around this if for some reason you can't.
As mentioned before, the "feet" child object must be a little bit under the player- this is because when the Raycast2d "fires" it will find the FIRST object with a collider it hits- so if you have the start of the raycast hit a collider on the player then it will always show the raycast hitting the player, not a platform. So to get around this have the raycast fire from the feet- and have the feet position a tiny bit below the player. To help with this I added a print statement that will print to the console which object the raycast is hitting, if it doesn't say "Platform" then you need to lower the feet! :)
Furthermore, I'm not sure how you are moving your platforms- but if the Velocity of the platform always prints "0,0" then its because you are moving the platforms position directly, instead of moving it by forces (this happened to me when I was testing this script). I got around this by moving the platform by "AddForce". Also depending on the "Mass" of your platforms, you may need to drastically increase the "xForceDampener" which I should of actually labeled "xForceMultiplier" as I found while testing I needed to set it much higher- like at 60, instead of 0.25 which I previously thought would be okay..
Hope this helped :D
EDIT: Linking the unity docs for 2D Raycasts in case you need
http://docs.unity3d.com/ScriptReference/Physics2D.Raycast.html
Hey everyone and thanks for taking the time.
I'm currently working on a 2D something in Unity and have the following problem:
We have a spawner that periodically creates 'thing':
public float spawnTime = 3;
public Object thing; //public GameObject thing;
// Use this for initialization
void Start () {
InvokeRepeating("spawn", spawnTime, spawnTime);
}
void spawn ()
{
Vector3 pos = new Vector3(-14, 0, 0);
Quaternion ori = new Quaternion(0, 0, 0, 0);
Instantiate(thing, pos, ori);
}
And it does a good job. Meet 'thing':
void Start () {
InvokeRepeating("move", 0.1f, 0.1f);
}
void move()
{
transform.Translate(0.2f, 0, 0);
if (this.transform.position.x > 14)
{
Destroy(this); //Destroy(GameObject);
So basically a 'thing' is created, hauls itself from -14 to 14 and then suffocates.
Problem: As soon as the first 'thing' is deleted the newly created (not the ones already moving) dont ever move from -14.
I guess I haven't properly specified which method to call periodically but wasn't able to find a proper solution in the unity3d top-down-shooter or similar examples.
Thanks again.
€: After applying the proposed changes the spawner stops creating 'thing's after 3 cycles of creation.
€: My mistake was not using the 'thing' as a prefab (dragging it into "assets" as described in the beautiful answer) so I was referencing a soon to be deleted instance of my 'thing'. Dragged it to hell (assets) and was happy ever after.
I recreated you setup with just these scripts and what I got is that the objects just didn't get destroyed, but newly spawned moved properly until 14.
The reason they didn't get destroyed though is, that you called Destroy(this); which destroys the script component, not the gameobject. So this should be Destroy(gameObject); instead. With this fix it runs just fine for me.
Also, if there is no special reason to use object, your public Object thing; should better be public GameObject thing;.
Edit:
How I set it up:
Created a Empty (renamed it to Spawner)
Created a Sphere (as my thing, renamed it to Ball)
Made Ball a prefab by dragging it into the assets folder
Deleted the Ball from the scene
Created a Spawner script and put your spawning code into it
Created a Ball script and put your move code into it
Made the two changes to the scripts
Put the Spawner script on the spawner gameobject
Dragged the Ball prefab on into the public GameObject thing of Spawner
Put the Ball script on the ball prefab
Done.
I'm fairly new to C#(basically first time using, having to script for this project), I'm trying to teleport (from off screen to on) an object in my game(i'm using a cube for a simple object, in which I will use inkscape to create a 'better' object in its place when it works)will add cube2 later, just focusing on getting this working.
The aim is to teleport an object to where to my 'Bumber' prefab (the floor), based upon the player clicking a position on the 'Bumber' and spawn where the mouse position was on that 'Bumber' and if not on the 'Bumber' don't spawn at all(haven't go to bumber check yet), which will trigger an event, causing the player to lose.
When I was playing the game before, when I clicked, the cube would only despawn and then throw an error at me and not spawn in at the cursors position
I have my 'cube' prefab (dragged from hierarchy into Resources folder, which has the spawn script component). When I go back into unity, I get the error:
(32, 37) the name 'cube' doesn't exist in current context
(32,25) The best overloaded method match for `UnityEngine.Object.Instantiate(UnityEngine.Object, UnityEngine.Vector3, UnityEngine.Quaternion)' has some invalid arguments
(32, 25) Argument #1' cannot convertobject' expression to type `UnityEngine.Object'
I've tried for hours to fix this script, looking at unity database and to no avail.
using UnityEngine;
using System.Collections;
public class Spawn : MonoBehaviour {
public int trapCount;
void Start ()
{
trapCount = 0;
GameObject cube =(GameObject)Instantiate((GameObject)Resources.Load("cube"));
}
void Update ()
{
if (Input.GetMouseButtonDown (0))
{
Spawner ();
trapCount++;
}
}
void Spawner()
{
Vector3 mousePosition = Input.mousePosition;
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if(trapCount == 0)
{
Instantiate(cube, transform.position, Quaternion.identity); //getting error here, I don't care about rotation value, don't want to rotate at all, but doesn't like it, if it doesn't have anything there.
}
else if (trapCount >= 1)
{
Debug.Log("Trap limit reached!");
}
}
}
C# please, also, if you could, explain what you're doing, thank you kindly!
Always (well, almost always) believe what the error messages tell you.
(32, 37) the name 'cube' doesn't exist in current context
You get this for the line
Instantiate(cube, transform.position, Quaternion.identity);
At that point there is no cube anywhere within the scope of the method. You have your
GameObject cube =(GameObject)Instantiate((GameObject)Resources.Load("cube"));
in your Start() method, but it only exist within that method. It's not a member of the class for example. Making it a member would solve that problem.
And that is more than likely also the cause of the subsequent errors. If it doesn't know what a cube is, it has no idea what to do with it as an argument of Instantiate().
If you're entirely new to C# the biggest favour you could do yourself is to pick up a good book. Unity will allow you to get pretty far by hacking away at it, but there will be a point where there's simply no substitute for learning the language. It will help you tremendously.
Good luck.
I'm trying to have my game spawn enemies when ever the player reaches a way point.
Right now, I have this functionality working. When my player gets to the first way, the enemies spawn. He only moves on once he has killed all of the enemies on the screen are dead. However, when he gets to the second way point, no enemies spawn.
Right now, in my collision class I call the following line of code:
Destroy(gameObject)
Whilst this work for the first way point, the second one wont spawn anything as the game object my collision class has been attached to has been destroyed. However, all of my enemies are prefabs and I thought the destroy function would only destroy that instance of the prefab. No matter when you called the instantiate method, it would only destroy that instance.
I'm spawning my enemies with the following method:
public GameObject SpawnEnemies()
{
Vector3 _position = new Vector3(transform.position.x, transform.position.y, transform.position.z);
// instantiate particel system
Instantiate(_particle, _position, Quaternion.identity);
_clone = (GameObject)Instantiate(_enemy, _position, transform.rotation);
_ai = _clone.GetComponent<HV_BaseAI>();
_ai._waypoints = _wayPoints;
return _clone;
}
Then I'm finding out how many of the enemies are still alive with the following code in my collision method:
GameObject g, f; // enemies I want to spawn
g = GameObject.FindGameObjectWithTag("SectionController");
f = GameObject.FindGameObjectWithTag("SectionController");
HV_SectionController tempSectionControllerGroundEnemies, tempSectionControllerFlyingEnemies;
tempSectionControllerGroundEnemies = g.GetComponent<HV_SectionController>();
tempSectionControllerFlyingEnemies = f.GetComponent<HV_SectionController>();
tempSectionControllerGroundEnemies._numberOfGroundEnemies.Remove(gameObject); // remove enemies from list
tempSectionControllerFlyingEnemies._numberOfFlyingEnemies.Remove(gameObject);
//Destroy(gameObject);
_numberOfGroundEnemies = tempSectionControllerGroundEnemies._numberOfGroundEnemies.Count;
_numberOfFlyingEnemies = tempSectionControllerFlyingEnemies._numberOfFlyingEnemies.Count;
Then when I want to move on I do the following check:
if (_groundEnemiesRemaining == 0)
{
MoveToNextSection();
_sectionStartTime = Time.time;
}
i know the above line is checking only one type of enemy at the moment, but its the ground enemies I'm having issues with at the moment.
Does anyone know how I can delete the enemy prefab I'm spawning from my first section, once they've been hit, then have it respawn at the next section without the error:
The object of type 'GameObject' has been destroyed but you are still
trying to access it.
My guess would be that the gameobjects are being "destroyed" by two different collision events. First one destroys it, second throws the error.
What I've done in similar situations is put the Destroy code within the object being destroyed. Then from within your collision class, use gameObject.SendMessage(string) to send a message to the actual object, which destroys it.
Without seeing further code I can't speculate as to the origin of the error, but the above is my best guess. SendMessage can also take a DontRequireReceiver parameter, so it won't pop an error if two collision events try to send it a message.
Instead of destroying them u can just disable them using gameObject.SetActive() !