I'm making a Game Called UnityCraft and I tried making a way to switch blocks!
Here is my Code:
using UnityEngine;
using System.Collections;
public class BuildScript : MonoBehaviour {
RaycastHit hit;
public int blockSelected = 1;
public Transform prefab;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if(Input.GetButtonDown(1)){
blockSelected = 1;
}
if(Input.GetButtonDown(2)){
blockSelected = 2;
}
if(blockSelected == 1){
prefab = dirt;
}
if(blockSelected == 2){
prefab = brick;
}
Ray ray = camera.ViewportPointToRay (new Vector3 (0.5f, 0.5f, 0));
Vector3 G = new Vector3 (Mathf.Round (hit.point.x), Mathf.Ceil (hit.point.y), Mathf.Round (hit.point.z));
if (Physics.Raycast (ray, out hit)) {
if (Input.GetMouseButtonDown (0)) {
Destroy (hit.collider.gameObject);
print ("Block Destroyed!");
}
if (Input.GetMouseButtonDown (1)) {
Instantiate (prefab, G, Quaternion.identity);
}
}
}
}
I have a prefab called brick and one called dirt, and they are linked to blocks.
I'm assuming that the problem you're referring to is in the line
if(Input.GetButtonDown(1)){
That won't work, because GetButtonDown does not have an integer argument. It takes a string, which you can find or define in the input manager.
From your code I do take it that you want to simply use number keys? In that case, don't use the GetButton calls, but use GetKey instead. So change your code to something like
if(Input.GetKeyDown(KeyCode.Keypad1)){
for the case where pressing 1 should trigger something.
Related
I am making my first foray into AI, specifically AI that will follow the player.
I am using the A* Path finding project scripts, but used the Brackeys tutorial for the code
https://www.youtube.com/watch?v=jvtFUfJ6CP8
Source in case needed.
Here's the code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class EnemyAI : MonoBehaviour
{
public Transform Target;
public float speed = 200f;
public float NextWayPointDistance = 3f;
Path path;
int currentWaypoint = 0;
bool ReachedEndOfpath = false;
Seeker seeker;
Rigidbody2D rb;
public Transform EnemyGraphics;
// Start is called before the first frame update
void Start()
{
seeker = GetComponent<Seeker>();
rb = GetComponent<Rigidbody2D>();
InvokeRepeating("UpdatePath", 0f, 1f);
}
void UpdatePath()
{
if (seeker.IsDone())
seeker.StartPath(rb.position, Target.position, OnPathComplete);
}
void OnPathComplete(Path p)
{
if (!p.error)
{
path = p;
currentWaypoint = 0;
}
}
// Update is called once per frame
void fixedUpdate()
{
if(path == null)
return;
if(currentWaypoint >= path.vectorPath.Count)
{
ReachedEndOfpath = true;
return;
}
else
{
ReachedEndOfpath = false;
}
Vector2 Direction = ((Vector2)path.vectorPath[currentWaypoint] - rb.position).normalized;
Vector2 Force = Direction * speed * Time.fixedDeltaTime;
rb.AddForce(Force);
float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]);
if(distance < NextWayPointDistance)
{
currentWaypoint++;
}
if(rb.velocity.x >= 0.01f)
{
EnemyGraphics.localScale = new Vector3(-1f, 1f, 1f);
}else if(rb.velocity.x <= 0.01f)
{
EnemyGraphics.localScale = new Vector3(1f, 1f, 1f);
}
}
}
How I tried to fix the issue:
I thought it could've been a problem with the speed so I increased it to 10000000, and still nothing
Next I thought it was a problem with the Rigidbody2d so I check there, found the gravity scale was set at 0, so I increased it to 1. It made my enemy fall to the ground but still no movement.
I thought it could've been a problem with the mass and drag, so I set Linear drag and Angular drag to 0, and also set mass to 1. Still nothing.
I set the body type to kinematic, pressed run, nothing. Set the body type to static, pressed run, nothing. Set the body type to Dynamic, pressed run, still nothing.
I tried to make a new target for the enemy to follow, dragged the empty game object i nto the target, pressed run and still didn't move.
I am at a loss on how to fix this.
Please help?
Looks like maybe a typo? You have:
// Update is called once per frame
void fixedUpdate()
{
but the method is called FixedUpdate(), with a big F in front. You have fixedUpdate, which is NOT the same.
Im working on bomb system in Unity 3D. I have uploaded some explosion effects from Unity Asset Store and I want implement them into my project. I want to plant bomb with key "K" then wait 3sec to detonate it with explosion effect and give some damage to nearby objects. The problem is that explosion appears in such different position as it should. In my opinion this is Editor problem , the code looks fine. I will give you some screenshoots(https://drive.google.com/file/d/19Yzymch9RdTa-E6RkbvvyfzMWjMJHo52/view?usp=sharing)and my bomb script :
public class BoombScript : MonoBehaviour
{
public GameObject boombEffect;
[SerializeField]
private float radius;
[SerializeField]
private float force;
[SerializeField]
private int explosiveDamage;
public void Explode()
{
Instantiate(boombEffect, transform.position, Quaternion.identity);
Debug.Log("Transform" + transform);
Debug.Log("Position" + transform.position);
Collider[] colliders = Physics.OverlapSphere(transform.position, radius);
foreach(Collider rangedObject in colliders)
{
GateScript Gate = rangedObject.GetComponent<GateScript>();
Rigidbody rb = rangedObject.GetComponent<Rigidbody>();
if(Gate != null)
{
Gate.GateDestroy(explosiveDamage);
}
if(rb != null)
{
rb.AddExplosionForce(force, transform.position, radius);
}
}
}
public IEnumerator WaitForExplode()
{
yield return new WaitForSeconds(3f);
Explode();
}
}
In my opinion this is Editor problem , the code looks fine.
In general I would always doubt such a statement.
I can only guess but if I understand you correctly you rather want to use the position of your object the moment when you start the Coroutine, not the one after 3 seconds:
// instead of using the current "reansform.position" rather
// use the pre-stored position passed in via parameter
public void Explode(Vector3 position)
{
Instantiate(boombEffect, position, Quaternion.identity);
Debug.Log("Transform" + transform);
Debug.Log("Position" + position);
Collider[] colliders = Physics.OverlapSphere(position, radius);
foreach(Collider rangedObject in colliders)
{
GateScript Gate = rangedObject.GetComponent<GateScript>();
Rigidbody rb = rangedObject.GetComponent<Rigidbody>();
if(Gate != null)
{
Gate.GateDestroy(explosiveDamage);
}
if(rb != null)
{
rb.AddExplosionForce(force, position, radius);
}
}
}
// If really needed you could still also have an overload
// e.g. if at some places you actually call this method from the outside
// immediately
public void Explode()
{
Explode(transform.position);
}
public IEnumerator WaitForExplode()
{
// Store the position when the routine is started
var position = transform.position;
yield return new WaitForSeconds(3f);
// Instead of using the current position rather pass in
// the previously stored one of when the routine was started
Explode(position);
}
So, this code just isn't responding for whatever reason. I have an enemy that I'm trying to get to face the player at all times (The enemy swoops over the player's head back and forth periodically). But other than making a flip happen whenever a timer hits 0, which doesn't really work all that well, I can't get the Flip function to work. I know the Flipper function is fine; I already tested it out and everything. I'm just not sure how to tell the enemy that when the player is to the left of it, to turn, and vice versa.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class dragoonDetection : MonoBehaviour {
private Rigidbody2D rb;
private Animator anim;
public Transform Player;
private bool facingRight = true;
void Start ()
{
rb = GetComponent<Rigidbody2D> ();
anim = GetComponent<Animator> ();
}
void Update()
{
Flip();
}
void Flip()
{
if (Player.transform.localScale.x > 0) {
transform.localScale = new Vector3 (1.69f, 1.54f, 1f);
}
if (Player.transform.localScale.x < 0) {
transform.localScale = new Vector3 (-1.69f, 1.54f, 1f);
}
}
void Flipper()
{
facingRight = !facingRight;
Vector2 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
Got any ideas? I'd rather avoid using FindGameObject because it's not actually looking for the player script. It's looking for a child transform with no script attached to the player. And because I have two different player GameObjects that you can switch to anytime in the game, it wouldn't really work for me in that regard.
You will need to perform a check of some sort against the players position with the bird position if you want it to face the player at all times. A barebones method would just be to compare the x-positions of the two objects and change the scale accordingly.
void Update()
{
transform.localScale = new Vector3(getDir()*1.69f, 1.54f, 1);
}
private int getDir()
{
if (player.transform.position.x < transform.position.x)
return -1;
else
return 1;
}
You should throw some additional checks in here to keep it from updating the scale every frame when there is no change.
I am working on a 2D game project. I would like the user to be able to hit the ball when he presses on the "space" key. I assigned;
Circle collider 2D & Rigidbody 2D to the ball
Rigidbody 2D & Box Collider 2D to the hero
Edge Collider 2D to the baseball bat.
Here is my script which I have called "KickTheBall.cs":
using UnityEngine;
using System.Collections;
public class KickTheBall : MonoBehaviour {
public float bounceFactor = 0.9f; // Determines how the ball will be bouncing after landing. The value is [0..1]
public float forceFactor = 10f;
public float tMax = 5f; // Pressing time upper limit
private float kickStart; // Keeps time, when you press button
private float kickForce; // Keeps time interval between button press and release
private Vector2 prevVelocity; // Keeps rigidbody velocity, calculated in FixedUpdate()
[SerializeField]
private EdgeCollider2D BatCollider;
private Rigidbody2D rb;
void Start () {
rb = GetComponent<Rigidbody2D>();
}
void FixedUpdate () {
if(kickForce != 0)
{
float angle = Random.Range(0,20) * Mathf.Deg2Rad;
rb.AddForce(new Vector2(0.0f,
forceFactor * Mathf.Clamp(kickForce, 0.0f, tMax) * Mathf.Sin(angle)),
ForceMode2D.Impulse);
kickForce = 0;
}
prevVelocity = rb.velocity;
}
void Update(){
if(Input.GetKeyDown (KeyCode.Space))
{
kickStart = Time.time;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if(hit.collider.name == "Ball") // Rename ball object to "Ball" in Inspector, or change name here
kickForce = Time.time - kickStart;
}
}
}
public void KickBall(){
BatCollider.enabled = true;
}
void OnCollisionEnter(Collision col)
{
if(col.gameObject.tag == "Ground") // Do not forget assign tag to the field
{
rb.velocity = new Vector2(prevVelocity.x,
-prevVelocity.y * Mathf.Clamp01(bounceFactor));
}
}
}
However, I am unable to kick the ball when I press the space key. The ball is just bouncing because of colliders. What am I missing?
Check my result:
I would advocate something more like this to start with. This is the script you would add onto your baseball bat.
Part 1:
using UnityEngine;
using System.Collections;
public class KickTheBall : MonoBehaviour
{
public float forceFactor = 10f;
private float kickForce = 50f;
void OnCollisionEnter(Collision col)
{
if(col.gameObject.tag == "Ball") // Do not forget assign tag to the field
{
rb = col.gameobject.GetComponent<Rigidbody>();
rb.AddForce(transform.right * kickForce);
}
}
}
I have simplified your AddForce function for demonstration purposes. Feel free to replace it with your more complex AddForce function if everything is working.
Part 2:
If you really want to include the part where holding the space button makes the hit stronger, then add this:
void Update()
{
if(Input.GetKey(KeyCode.Space))
{
kickForce += 0.5f;
}
}
and add at the end of the oncollisionenter
kickForce = 0;
What this will do is build up force while you hold the space button down. After a successful hit the force will reset to 0. So subsequent collisions will not result in a hit until the space button is held again.
Let me know if this did anything for you.
I solved the issue with the help of #TylerSigi. I updated my script file with these codes:
using UnityEngine;
using System.Collections;
public class KickTheBall : MonoBehaviour {
public float forceFactor = 10f;
private float kickForce = 0f;
private EdgeCollider2D BatCollider;
public GameObject Ball;
void Start () {
}
void Update()
{
if (Input.GetKey (KeyCode.Space)) {
kickForce = 1000;
} else {
kickForce = 0;
}
}
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.tag == "Enemy") // Do not forget assign tag to the field
{
Ball.GetComponent<Rigidbody2D>().AddForce(transform.right * kickForce);
}
}
}
what i't trying to achieve is have my turrent rotate and follow an "Enemy".
At the moment it detects the enemy but it is not rotating and I don't know why.
The bullet it a prefab i drag in, there has to be a better way to do this? Anyone have any suggestions please?
At the moment the bullet never triggers, but the log Shoot and does...and the rotate doesn't work.
Here is what i have
using UnityEngine;
using System.Collections;
public class TurretController : MonoBehaviour {
public Rigidbody bulletPrefab;
private Transform target;
private GameObject bullet;
private float nextFire;
private Quaternion targetPos;
void OnTriggerEnter(Collider otherCollider) {
if (otherCollider.CompareTag("Enemy"))
{
Debug.Log ("in");
target = otherCollider.transform;
StartCoroutine ("Fire");
}
}
void OnTriggerExit(Collider otherCollider) {
if (otherCollider.CompareTag("Enemy"))
{
Debug.Log ("out");
target = null;
StopCoroutine("Fire"); // aborts the currently running Fire() coroutine
}
}
IEnumerator Fire()
{
while (target != null)
{
nextFire = Time.time + 0.5f;
while (Time.time < nextFire)
{
// smooth the moving of the turret
targetPos = Quaternion.LookRotation (target.position);
transform.rotation = Quaternion.Slerp(transform.rotation, targetPos, Time.deltaTime * 5);
yield return new WaitForEndOfFrame();
}
// fire!
Debug.Log ("shoot");
bullet = Instantiate(bulletPrefab, transform.position, transform.rotation) as GameObject;
//bullet.rigidbody.velocity = transform.forward * bulletSpeed;
}
}
}
I tried to change the instantiate part by using this instead
bullet = (GameObject)Instantiate(bulletPrefab, transform.position, transform.rotation);
bullet.GetComponent<Bullet>().target = target.transform;
But then i just get errors like "InvalidCastException: Cannot cast from source type to destination type.
TurretController+c__Iterator0.MoveNext () (at Assets/Scripts/TurretController.cs:44)"
BTW, here's the turret rotation code I used in my project (shared with permission):
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Turret : MonoBehaviour
{
[SerializeField]
private float turnRateRadians = 2 * Mathf.PI;
[SerializeField]
private Transform turretTop; // the gun part that rotates
[SerializeField]
private Transform bulletSpawnPoint;
private Enemy target;
void Update()
{
TargetEnemy();
}
void TargetEnemy()
{
if (target == null || target.health <= 0)
target = Enemy.GetClosestEnemy(turretTop, filter: enemy => enemy.health > 0);
if (target != null)
{
Vector3 targetDir = target.transform.position - transform.position;
// Rotating in 2D Plane...
targetDir.y = 0.0f;
targetDir = targetDir.normalized;
Vector3 currentDir = turretTop.forward;
currentDir = Vector3.RotateTowards(currentDir, targetDir, turnRateRadians*Time.deltaTime, 1.0f);
Quaternion qDir = new Quaternion();
qDir.SetLookRotation(currentDir, Vector3.up);
turretTop.rotation = qDir;
}
}
}
class Enemy : MonoBehaviour
{
public float health = 0;
private static HashSet<Enemy> allEnemies = new HashSet<Enemy>();
void Awake()
{
allEnemies.Add(this);
}
void OnDestroy()
{
allEnemies.Remove(this);
}
/// <summary>
/// Get the closest enemy to some transform, optionally filtering
/// (for example, enemies that aren't dead, or enemies of a certain type).
/// </summary>
public static Enemy GetClosestEnemy(Transform referenceTransform, System.Predicate<Enemy> filter=null)
{
// Left as an exercise for the reader.
// Remember not to use Vector3.Distance in a loop if you don't need it. ;-)
// return allEnemies[0];
}
}
First problem: the bullet prefab. The variable type is RigidBody. If you want to treat it as a game object, the variable must be a game object. Or you can instantiate it, cast to RigidBody, then use the .gameObject accessor. Like this:
((RigidBody)Instantiate(theRigidbody)).gameObject
Second problem: start simple with the rotation. If it's not working, don't get fancy yet. Start with something like this (an instant rotation toward the target):
Vector3 targetDirection = target.transform.position - transform.position;
targetDirection.y = 0; // optional: don't look up
transform.forward = targetDirection;
If it works, then add small pieces of additional complexity until it does exactly what you want. And if you don't get things figured out, give me a shout (a comment) on Monday. I've written turret-aiming code (including a maximum rotation speed), and I don't think my boss would mind if I upload it.