I have a little problem; I'm trying to develop my own endless jumping game. That's why I created an enemy type that can fire small projectiles. Every time it shoots, a sound should be played, but it won't. Debug: "Can not play disabled audio source."
Originally that only didn't work out for the prefabs, but recently it didn't work out for the first, original opponent either. (I kinda messd up spmething in the code)
Every proposed solution would help me a lot :)
I know, there are some posts about this topic out there, but none of this has helped yet...
The kinda messy code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FlyingEnemy : MonoBehaviour {
public float startTimeBtwShots;
private float timeBtwShots;
private Vector3 spawnPoint;
public AudioSource shot;
public Animator animator;
private float flyingSpeed;
private float turningPoint;
private bool moving;
public GameObject projectile;
public GameObject player;
void Start ()
{
turningPoint = Mathf.Abs(transform.position.x);
flyingSpeed = 0.008f;
timeBtwShots = startTimeBtwShots;
moving = true;
shot.enabled = true;
}
void Update ()
{
//Ignore, moves enemy from left to right and back
if (moving)
{
transform.position = new Vector3(transform.position.x + flyingSpeed, transform.position.y, transform.position.z);
}
if (transform.position.x >= turningPoint || transform.position.x <= turningPoint * -1)
{
flyingSpeed *= -1;
transform.position = new Vector3(transform.position.x + flyingSpeed, transform.position.y, transform.position.z);
if (flyingSpeed <= 0) transform.eulerAngles = new Vector3(0, 180, 0);
else if (flyingSpeed >= 0) transform.eulerAngles = new Vector3(0, 0, 0);
}
//Important part: projectile gets spawn and a bit earlier the sound should be played...
if (timeBtwShots <= 0)
{
spawnPoint = transform.position;
spawnPoint.y -= 0.35f;
Instantiate(projectile, spawnPoint, Quaternion.identity);
timeBtwShots = startTimeBtwShots;
animator.SetBool("Shooting", false);
}
else
{
timeBtwShots -= Time.deltaTime;
if (timeBtwShots <= 0.3)
{
shot.volume = 1;
shot.Play();
}
if (timeBtwShots <= 0.6)
{
animator.SetBool("Shooting", true);
}
}
if (player.transform.position.y >= transform.position.y + 5.5f)
{
Destroy(gameObject);
}
}
}
Thank you in advance!!
Ego | Jakob
You should try on your Start function: shot.gameObject.SetActive(true);
It can be the problem.
Related
GOAL
So I'm creating a top down arena battler type game, and I want you to be able to restart the game by pressing R.
PROBLEM
When I press R, the whole scene resets as it should, except all the enemies that were previously instantiated (and then destroyed) are spawned again, all at once.
CODE
This is the enemy spawning code :
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawn : MonoBehaviour
{
private float nextActionTime = 0.0f;
public float period = 5f;
public GameObject enemy;
void Update()
{
if (Time.time > nextActionTime ) {
nextActionTime += period;
GameObject clone = Instantiate(enemy, new Vector3(-1, 3, 0), Quaternion.identity);
clone.tag = "enemy";
}
}
}
This is the player code, responsible for restarting the scene (I have marked what I belive to be the relevantant sections with dashes) :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class PlayerController : MonoBehaviour
{
public Rigidbody2D rb;
public GameObject Shield;
public GameObject ShieldInstance;
public float moveSpeed = 4.3f;
public float sheildSpeed = 5f;
Vector2 movement;
AudioSource woop;
AudioSource waa;
----------------------------
GameObject[] enemies;
----------------------------
bool isDead = false;
void Start() {
woop = GameObject.Find("Main Camera/ShieldSFX").GetComponent<AudioSource>();
waa = GameObject.Find("Main Camera/DefeatSFX").GetComponent<AudioSource>();
}
void Update()
{
--------------------------------------------------------------
enemies = GameObject.FindGameObjectsWithTag("enemy");
--------------------------------------------------------------
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
Vector3 mouseScreen = Input.mousePosition;
Vector3 mouse = Camera.main.ScreenToWorldPoint(mouseScreen);
transform.rotation = Quaternion.Euler(0, 0, Mathf.Atan2(mouse.y - transform.position.y, mouse.x - transform.position.x) * Mathf.Rad2Deg - 90);
if (Input.GetMouseButtonDown(0))
{
if (ShieldInstance != null || transform.GetChild(0).GetComponent<SpriteRenderer>().enabled == false) { return; }
woop.Play();
ShieldInstance = Instantiate(Shield, transform.position + transform.forward + transform.up, transform.rotation);
ShieldInstance.transform.parent = transform;
}
if (Input.GetMouseButtonUp(0))
{
if (ShieldInstance == null) { return; }
ShieldInstance.transform.parent = null;
ShieldInstance.GetComponent<ShieldController>().LaunchForward(sheildSpeed);
Destroy(ShieldInstance, 2.3f);
}
-------------------------------------------------------------------------------
if (Input.GetKey("r")) {
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
foreach (GameObject one in enemies) {
Destroy(one);
}
}
-------------------------------------------------------------------------------
}
void FixedUpdate() {
if (!isDead) {
rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
}
}
void OnCollisionEnter2D(Collision2D other) {
if (other.gameObject.tag == "enemy") {
waa.Play();
GameObject.Find("Canvas/gameover").GetComponent<Text>().enabled = true;
transform.GetChild(0).GetComponent<SpriteRenderer>().enabled = false;
GetComponent<PolygonCollider2D>().enabled = false;
}
}
}
And this is the enemy code :
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyFollow : MonoBehaviour
{
public float moveSpeed;
public ParticleSystem explode;
AudioSource boom;
Vector2 movement;
GameObject player;
Rigidbody2D rb;
SpriteRenderer sr;
PolygonCollider2D pc;
void Start() {
rb = GetComponent<Rigidbody2D>();
sr = transform.GetChild(0).GetComponent<SpriteRenderer>();
pc = GetComponent<PolygonCollider2D>();
player = GameObject.Find("Player");
boom = GameObject.Find("Main Camera/ExplodeSFX").GetComponent<AudioSource>();
}
void Update()
{
Vector2 difference = (player.transform.position - new Vector3(2, .5f, 0)) - transform.position;
if (difference.x > 0) {
movement.x = 1;
} else if (difference.x < 0){
movement.x = -1;
} else {
movement.x = 0;
}
if (difference.y > 0) {
movement.y = 1;
} else if (difference.y < 0){
movement.y = -1;
} else {
movement.y = 0;
}
rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
}
void OnCollisionEnter2D(Collision2D other) {
if (other.gameObject.tag == "shield") {
StartCoroutine(ExplodeF());
}
}
private IEnumerator ExplodeF() {
explode.Play();
boom.Play();
sr.enabled = false;
pc.enabled = false;
yield return new WaitForSeconds(explode.main.startLifetime.constantMax);
Destroy(gameObject);
}
}
I would really appreciate any help!
If you want / need more details, just leave a comment :)
The problem is in Time.time, it is time since the start of the application, not since the start of the scene. So if you were in the game 30 secs, Time.time is 30 secs. If you reload the scene it is still 30 secs.
You have to count the time passed since entering the scene. Then it won't respawn all the enemies on scene reload.
When you restart the scene, everything is being destroyed and reset back except for the time.
The problem in your code is in the enemy spawner. Time.time returns the time passed since you started the game (and not since the scene was loaded). So, after the restart the if condition is true and the enemies are spawned.
If you want to count the time (in seconds) since the scene was loaded, what you can do is to add a variable in the enemy spawner class that would count the time
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawn : MonoBehaviour
{
private float nextActionTime = 0.0f;
private float timeSinceStart = 0;
public float period = 5f;
public GameObject enemy;
void Update()
{
if (timeSinceStart > nextActionTime ) {
nextActionTime += period;
GameObject clone = Instantiate(enemy, new Vector3(-1, 3, 0), Quaternion.identity);
clone.tag = "enemy";
}
timeSinceStart += Time.deltaTime;
}
}
See Time.deltaTime
So I've been following a tutorial on making a 2D game in Unity (I'm a complete newbie, this is my first contact with programming), and I wanted to add a feature to the game(bold).
The "heart system" that I added works correctly (the number of empty hearts is equal to damage taken by player), but it caused my player to transform his position in a wierd way. You can see, that there are boundries set (maxHeight =3,2, minHeight=-3,2), and the value of his movement as well (Yincrement = 3.2) and yet, after pressing up or down arrowkeys he seems to change Y position by around 4.67.
Here's the player script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Player : MonoBehaviour
{
private Vector2 targetPos;
public float Yincrement;
public float speed;
public float maxHeight;
public float minHeigth;
public int health = 3;
public int numOfHearts;
public Image[] hearts;
public Sprite heartFull;
public Sprite heartEmpty;
public GameObject effect;
public Image healthDisplay;
private void Update()
{
for (int i = 0; i < hearts.Length; i++)
{
if (i < health)
{
hearts[i].sprite = heartFull;
}
else
{
hearts[i].sprite = heartEmpty;
if (i < numOfHearts)
{
hearts[i].enabled = true;
}
else
{
hearts[i].enabled = false;
}
}
if (health <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.UpArrow) && transform.position.y < maxHeight)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y + Yincrement);
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && transform.position.y > minHeigth)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y - Yincrement);
}
}
}
}
Main Issue
The main problem is your for loop. It should only be used for updating the UI - not for multiple times calling your movement and Scene reload! You should close your for loop earlier.
for (int i = 0; i < hearts.Length; i++)
{
// you can also reduce these to single lines
hearts[i].enabled = i < numOfHearts;
if(i < numOfHearts) hearts[i].sprite = i < health ? heartFull : heartEmpty;
} // <-- ALREADY CLOSE IT HERE
if(health <= 0) ...
...
Position clamping
Your clamping of the position is extremely insecure! Imagine the current position being 3.1 which is still < maxHeight so you add the Yincrement once and it results in a maximum possible height of 6.3! That's not what you wanted.
You should clamp directly on the targetPosition not on the current transform.position. You could for example use Mathf.Clamp for making sure the targetPos.y always stays within the given range.
Also since both cases do something very similar I would reduce this to only one movement using a simply int variable for setting the direction:
...
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
var move = 0;
var targetPosY = targePos.y;
if (Input.GetKeyDown(KeyCode.UpArrow) && targetPosY < maxHeight)
{
move = 1;
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && targetPosY > minHeigth)
{
move = -1;
}
// Handle the movement according to the direction
// in one single code block for both directions
if(move != 0)
{
Instantiate(effect, transform.position, Quaternion.identity);
// increase/decrease the targetPosY according to the move direction
targetPosY += move * Yincrement;
// Now make sure it is within the range
targetPosY = Mathf.Clamp(targetPosY, minHeight, maxHeight);
// finally assign the new target position
targetPos = new Vector2(transform.position.x, targetPosY);
}
Could you please specify in detail what your game is about and what you are trying to do in each part of the script. I might be able to help you then. Also, if this is your first contact with programming, this is way to advanced. Start with something simpler and first understand the basic concepts of programming before moving on. Here is a good tutorial series to learn c# programming for absolute beginners.
https://www.youtube.com/watch?v=pSiIHe2uZ2w
I am not sure what you are trying to do but why is your movement control within your for loop. That might be why your are messing up. Try removing all of this code out of the for loop.
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.UpArrow) && transform.position.y < maxHeight)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y + Yincrement);
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && transform.position.y > minHeigth)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y - Yincrement);
}
Also this code should be after you check if the player has moved.
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
Here is a tip to make movement a little easier. Use transform.translate instead of Vector2.MoveTowards. Transform.translate takes in 3 floats and changes you player's position by the three floats represented as a vector.
Here is an example
transform.translate(0,2,0);
This will change the players y position by 2. I thing your code should look like this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Player : MonoBehaviour
{
private Vector2 targetPos;
public float Yincrement;
public float speed;
public float maxHeight;
public float minHeigth;
public int health = 3;
public int numOfHearts;
public Image[] hearts;
public Sprite heartFull;
public Sprite heartEmpty;
public GameObject effect;
public Image healthDisplay;
private void Update()
{
for (int i = 0; i < hearts.Length; i++)
{
if (i < health)
{
hearts[i].sprite = heartFull;
}
else
{
hearts[i].sprite = heartEmpty;
if (i < numOfHearts)
{
hearts[i].enabled = true;
}
else
{
hearts[i].enabled = false;
}
}
}
if (health <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
if (Input.GetKeyDown(KeyCode.UpArrow) && transform.position.y < maxHeight)
{
transform.Translate(0,Yincrement,0)
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && transform.position.y > minHeigth)
{
Instantiate(effect, transform.position, Quaternion.identity);
transform.Translate(0,Yincrement,0);
}
}
}
After I receive your reply, I possibly could help but there are no guarantees because I only have about a years worth of experience too.
I'm trying to programming movements of a main FPS character with a Rigidbody.
Camera and ZQSD displacements works well but jumping while moving is being very restricted :
Immobile jumping : Y (min) = 1; Y (max) = 2.7;
Mobile jumping : Y (min) = 1; Y (max) = 1.9;
It's very frustrating, especially for a platform game. Personnaly, I'd like to get same result for both actions.
Here's my C# code :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FPSMovement : MonoBehaviour {
public float walkAcceleration = 150f;
public float maxWalkSpeed = 10f;
public float jumpVelocity = 500f;
public float maxSlope = 45f;
private Rigidbody rb;
private Vector2 horizontalMovement;
private bool isGrounded = false;
private bool doubleJumped = false;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody> ();
}
// Update is called once per frame
void Update () {
if(rb.velocity.magnitude > maxWalkSpeed){
rb.velocity = Vector3.ClampMagnitude(rb.velocity, maxWalkSpeed);
}
transform.rotation = Quaternion.Euler (0, GetComponentInChildren<FPSCamera>().currentYRotation, 0);
// Entrées clavier ZQSD déplaçant le joueur
float x = 0, y = 0, z = 0;
if (Input.GetKey (KeyCode.Z)) {
x += 1f;
}
if (Input.GetKey (KeyCode.S)) {
x -= 1f;
}
if (Input.GetKey (KeyCode.Q)) {
z -= 1f;
}
if (Input.GetKey (KeyCode.D)) {
z += 1f;
}
// Arrêt prompt du glissement
if ((!Input.GetKey (KeyCode.Z) && !Input.GetKey (KeyCode.S) && !Input.GetKey (KeyCode.Q) && !Input.GetKey (KeyCode.D))
&& isGrounded) {
rb.velocity /= 1.1f;
}
// Saut du joueur
if (Input.GetKey (KeyCode.Space) && isGrounded) {
rb.AddForce (0, jumpVelocity, 0);
// Deuxième saut
}
if (Input.GetKey (KeyCode.Space) && !doubleJumped) {
rb.AddForce (0, jumpVelocity, 0);
doubleJumped = true;
}
rb.AddRelativeForce (z * walkAcceleration, 0, x * walkAcceleration);
}
void OnCollisionStay(Collision other) {
foreach (ContactPoint contact in other.contacts) {
if (Vector3.Angle (contact.normal, Vector3.up) < maxSlope) {
isGrounded = true;
doubleJumped = false;
}
}
}
void OnCollisionExit() {
isGrounded = false;
}
}
I also have impression that player hovers when moving in the airs, but this is not the main question and above all just an impression.
A simple issue with few words to explain, but a real problem to resolve for this type of gaming !
May I ask you, Stackoverflow people to gently help me, a thousand thanks by advance mates.
PS: My native language is the french, so please, don't lose too much time on grammar or other english subtlety :)
It is because you clamp the total velocity of the player where you should only clamp the horizontal speed. If you walk at maximum speed and jump, your jump speed is added to the total velocity and your character should actually be travelling at a higher speed than the maximum walking speed. To fix it, you do this:
// Isolate the horizontal component
Vector3 horizontalVelocity = rb.velocity;
horizontalVelocity.y = 0;
if (horizontalVelocity.magnitude > maxWalkSpeed) {
// Clamp the horizontal component
Vector3 newVelocity = Vector3.ClampMagnitude(horizontalVelocity, maxWalkSpeed);
// Keep the original vertical velocity (jump speed)
newVelocity.y = rb.velocity.y;
rb.velocity = newVelocity;
}
I'm working on a project where I'm trying to make my character move by jumping at an angle. Right now during the frame updates, the character will pivot back and forth and when the right key is pressed, they will jump. This code causes them to jump at an angle, but they always return to their original position.
Additionally, I have two characters who start on opposite sides of the stage, but when I start the game they teleport to the same position. I've spent a lot of time reviewing my code but I can't seem to get this to work. Any help you can provide?
using UnityEngine;
using System.Collections;
public class Freg : MonoBehaviour {
public GameObject Tounge;
public float gravity;
public float tempScale = 1;
public float MaxJump = 8f;
public float MinJump = 0.1f;
static float yVector = 0;
static float xVector = 0;
static bool grounded = true;
bool isleft = false;
Vector3 farthestleft;
Vector3 farthestright;
// Use this for initialization
void Start () {
farthestleft = new Vector3 (-33.7f, 50.2f, 24.8f);
farthestright = new Vector3 (22.56f, 54.83f, -15.12f);
}
void OnTriggerEnter (Collider other) {
if (other.GetComponent<Collider> ().tag == "Ground") {
grounded = true;
yVector = 0;
//xVector = 0;
Vector3 onGround = new Vector3 (transform.position.x, -4.86f, transform.position.z);
transform.position = onGround;
} else
grounded = false;
}
// Update is called once per frame
void Update () {
/*if (Input.GetKey (KeyCode.UpArrow) == true) {
Tounge.transform.localScale.Set (1, 0.5f, 1);
} else {
Tounge.transform.localScale.Set (1, 1, 1);
}*/
if (grounded == false) {
yVector -= gravity;
}
if (Input.GetKeyDown (KeyCode.UpArrow) == true && grounded == true) {
MinJump += 0.5f;
} else if (MinJump > 0.1f){
yVector += MinJump;
xVector += MinJump;
MinJump = 0.1f;
grounded = false;
}
Vector3 stuff = new Vector3 (transform.localPosition.y + xVector, transform.position.y + yVector, transform.position.z);
transform.position = stuff;
float t = Mathf.PingPong (Time.time * 0.5f * 2.0f, 1.0f);
transform.eulerAngles = Vector3.Lerp (farthestright, farthestleft, t);
}
}
it looks like you should update the current position during the if statements, rather than after that way on each update, the actual position is moving based on the decision rather than just the end of the loop.
public class AsteroidMovement : MonoBehaviour
{
public Vector2 speed;
public Vector2 direction;
private Vector2 movement;
private Vector3 TopScreenBound;
private Vector3 BottomScreenBound;
// Use this for initialization
void Start()
{
TopScreenBound = Camera.main.ViewportToWorldPoint(new Vector3(0f, 1f, 0f));
BottomScreenBound = Camera.main.ViewportToWorldPoint(new Vector3(0f, 0f, 0f));
}
// Update is called once per frame
void Update()
{
if (gameObject.transform.position.y >= TopScreenBound.y)
{
direction.y *= -1;
}
if (gameObject.transform.position.y <= BottomScreenBound.y)
{
direction.y *= -1;
}
movement = new Vector2(speed.x * direction.x, speed.y * direction.y);
}
void FixedUpdate()
{
rigidbody2D.velocity = movement;
}
}
I am trying to have asteroids in my game bounce off the edge of my screen and I have got it working decently, but after a few bounces, the asteroid/object gets "stuck" in the wall and glitches out the playing area.
Am I going about this wrong? I cannot see where in the code its making the asteroids stuck after a couple of bounces.Thanks in advance :)
You must fix your object's position to bounce inside the screen, if your object is already outside of the screen and it does not fully enter in screen space in the next frame, then your object is changing it's direction infinitely until it enters or leaves the screen.
Change this:
if (gameObject.transform.position.y >= TopScreenBound.y)
{
direction.y *= -1;
}
if (gameObject.transform.position.y <= BottomScreenBound.y)
{
direction.y *= -1;
}
To this:
if (gameObject.transform.position.y >= TopScreenBound.y)
{
gameObject.transform.position.y = TopScreenBound.y;
direction.y *= -1;
}
if (gameObject.transform.position.y <= BottomScreenBound.y)
{
gameObject.transform.position.y = BottomScreenBound.y;
direction.y *= -1;
}