Unity 2D tongue shooter - c#

I would like to use a characters tongue so instead of shooting a bullet, it goes toward the enemy, licks it, and comes back. I got this wording from this question: Unity shooting with Tongue 2d game (hasn't been answered and is 4+ years old). The only difference is my character moves.
I have this code from looking at a shooting tutorial so when you click the tongue prefab generates and is at the correct angle. I need it to grow on click and shrink back.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LineController : MonoBehaviour
{
public GameObject player;
private Vector3 target;
public GameObject crosshairs;
public GameObject tonguePrefab;
// Start is called before the first frame update
void Start()
{
Cursor.visible = false;
}
// Update is called once per frame
void Update()
{
target = transform.GetComponent<Camera>().ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transform.position.z));
crosshairs.transform.position = new Vector2(target.x, target.y);
Vector3 difference = target - player.transform.position;
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
if (Input.GetMouseButtonDown(0))
{
shootTongue(rotationZ);
}
}
IEnumerator shootTongue(float rotationZ)
{
GameObject t = Instantiate(tonguePrefab) as GameObject;
t.transform.position = new Vector2(target.x, target.y);
t.transform.rotation = Quaternion.Euler(0.0f, 0.0f, rotationZ);
yield return new WaitForSeconds(2000);
t.SetActive(false);
}
}
I was also trying to make it disappear after whatever time and with that it doesn't work at all?

If the prefab is just a line with a simple texture I suggest using a line renderer:
Create an empty transform that starts on the mouth and then it move untill it reaches the crosshair. The movement is done using Vector2.MoveTowards.
Your tongue will no be a line renderer component. This line renderer will have 2 points, the start which is static, and the end which you have to update on the Update() for example, to correspond to the positions of the empty transform in item 1.
If you really wish to expand a tongue gameobject instead, then you are going to need a bit of math, this answer has what you need but in 3D:
https://answers.unity.com/questions/473076/scaling-in-the-forward-direction.html

1) I suggest creating sprite/sprite sheet animations with Mecanim or relatively new Skeletal Animation with Anima2D. With these systems you can create nice animations, transitions, even animate the sphere collider to trigger collisions and actions, change active state of your objects, etc. and control the animation with very little code. This way you can get best effects in my opinion.
As tongue is not a bullet... :) You only need one. I don't think you want to Instantiate/Create prefabs every time you press the lick button. Instead you should just turn your tongue object on/off (gameObject.SetActive). You can also change your object active state within the animation. So if you don't know how to code it you can do most of it in the Animation window and use very simple code to play the animation when you press the lick button. Whenever a sphere collider touches something you can tell the Animator Controller to play a 'roll back' animation and it will transition nicely to the start position.
There are many tutorials about Mecanim, Animations, 2D Animations, Anima2D, Animator Controller out there.
2) If you need very good control over the tongue you could create a custom mesh and control it via script but this is far more difficult.
3) The reason why your object is not turning off is probably because you wrote WaitForSeconds(2000) so it will turn off after 2000 seconds - more than half an hour. You should also call it with StartCoroutine(shootTongue()) as it is a Coroutine. Again if you want to turn off the object don't create new ones every time. If you want to keep creating new objects you should Destroy the objects instead. Otherwise you will end up with a lot of deactivated tongues in your scene and I don't think you needs that many tongues.

Related

How would one add force to a rigidbody from a relative mouse position?

I have a game I am creating in Unity. It has a table with 30 cubes on it. I want a user to be able to shuffle the cubes on the table using their mouse/touch.
I am currently using a ray to get the initial table/cube hit point then accessing the rigidbody component on the cube to apply a force using AddForceAtPosition, happening in an OnMouseDrag. I am also pulling out my hair trying to figure out how to apply force in the direction from the mouse's last position to the hit point on the rigidbody of the cube.
Can someone please help me? An example would be great. I would share my code but I am a spaghetti code monster and fear criticism... Thanks much!
If I understood correctly, is something like this you want to achieve? https://www.youtube.com/watch?v=5Zli2CJGAtU&feature=youtu.be
If so, first you have to add your cubes and table to a new layermask, this step is only necessary if you don't want your ray to hit other colliders that might mess with your result.
After this you should add a tag to all the cubes you want to aplly the force, in my case I added a new tag called "Cubes".
You can search on youtube this if you don't know how to do it, there are plenty of tutorials to help you.
After that you create a new script and attach to the camera. Here is my script:
public class CameraForceMouse : MonoBehaviour
{
public LayerMask layerMask;
RaycastHit hit;
Vector3 lastPosition;
// Start is called before the first frame update
void Start()
{
lastPosition = new Vector3();
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0))
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 200f, layerMask))
{
if (hit.transform.CompareTag("Cubes"))
{
Rigidbody rb = hit.transform.GetComponent<Rigidbody>();
Vector3 force = (hit.point - lastPosition).normalized * 55f;
rb.AddForceAtPosition(force, hit.point);
}
lastPosition = hit.point;
}
}
}
}
Just want to say that there are a lot of ways to achieve the same thing, this is the way I decided to do, but there are probably other (maybe even better) ways of doing this.
Explaining the code:
The first thing is a public LayerMask that before you play the game you will have to select your camera in your scene and look for the script on the editor in the right side and select witch layerMask you added to your cubes and table.
If you don't know how raycast works, here is the documentation: https://docs.unity3d.com/ScriptReference/Physics.Raycast.html but you can also search on youtube.
The important thing here is the new vector being created to define the direction of the force.
First we declare a global variable called LastPosition, and for every frame that we are clicking, we will check for a collision, and if this collision happens, we will then create a new vector going out of the last position the mouse was to the position the mouse is now Vector3 force = (hit.point - lastPosition), than we normalize it because we only care about the direction and multiply it by the amount of force we want, you can make it as a nice variable but I just put it there directly.
Then, after you've applied the force to the position the ray hit the object, you have to define that this is now your last position, since you are going to the next frame. Remember to put it outside of the Tag check, because this is checking if the object we collided with is the cube we want to apply the force or if it is the table. If we add this line lastPosition = hit.point; inside of this check it will only assign a new lastPosition value when we hit the cube, and would lead to bugs.
I'm not saying my answer is perfect, I can think of a few bugs that can happen, when you click outside the table for example, but is a good start, and I think you can fix the rest.

Dead zones in a bullet trajectory - unity 2d

I’m making a small shooter for two players, everyone can shoot. The problem is that when a bullet flies at high speed, then there are certain zones in its trajectory in which it does not touch the object, and accordingly OnTriggerEnter2d does not work there. Here is the bullet flight script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet: MonoBehaviour
{
Rigidbody2d rb;
public int Player;
public float speed = 0.4f;
public Vector3 mode; // Direction of the bullet (Vector3.left, right)
// Start is called before the first frame update
void Start ()
{
rb = gameObject.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void FixedUpdate ()
{
rb.velocity = mode * speed;
}
}
High speed physics is always a hard nut to crack. Like Iggy mentions you can/should use continuous collision detection.
If you still have issues and still want fast projectiles it get harder. Here are some options.
You can lower the physics time step in time settings (very expensive not recommended but you can test).
You can make the bullet's colliders dynamically bigger. (Might be the simplest easiest solution). When you spawn bullets, calculate based on their speed how long a box collider should be and dynamically position it behind the bullet sorta like a trail (make sure the bullet has moved forward enough to fully expand so you don't hit things behind the player). The box collider's size should be the same as how much distance it moves every frame. That way after moving a tons in a frame all the positions the bullet has been in the frame will trigger a collision on the box collider. For bullet impact and sfx don't spawn them where the bullet is but at the point of contact.
You can calculate if a collision should have happened. (After the physics update check behind the projectile with like a raycast).
You can do your own physics or hybrid. Unity lets you simulate physics when ever you want manually instead of the normal physics update. With more control over that you could add more calculations for fast moving object.

How to drop a box to the mouse position?

I am trying to learn unity, and made my first own game and stucked at the beginning. The first idea was to drop a box (cube) to the mouse position. There are many videos and posts about getting the mouse position, and i tried to use them. My problem is, the mouse position i got is the camera's position, instead of the plane.
As you can see, it is kinda works, but it isn't fall to the plane.
https://prnt.sc/lmrmcl
My code:
void Update()
{
Wall();
}
void Wall()
{
if (Input.GetMouseButtonDown(0))
{
if (Input.GetMouseButtonDown(0))
{
wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
Rigidbody wallsRigidbody = wall.AddComponent<Rigidbody>();
wall.transform.localScale = new Vector3(0.6f, 0.6f, 0.6f);
wallsRigidbody.mass = 1f;
wallsRigidbody.angularDrag = 0.05f;
wallsRigidbody.useGravity = true;
wallsRigidbody.constraints = RigidbodyConstraints.FreezeRotation;
wall.transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
Debug.Log(Camera.main.ScreenToWorldPoint(Input.mousePosition));
BoxCollider wallsCollider = wall.AddComponent<BoxCollider>();
wallsCollider.size = new Vector3(1f, 1f, 1f);
}
}
How should i change my code to get the right position?
This isn't a direct answer to your question, but I'm hoping it'll still get you where you need to go.
Prefabs are your friends! I'd highly recommend leveraging them here instead of constructing a cube directly in code.
But first, make sure everything else is set up right. Go ahead and construct a cube by hand in the Editor and make sure that when you hit Play, it falls as you expect. It should, provided it has a Rigidbody, collider, and you have gravity enabled (true by default).
If that works, drag that cube from your Hierarchy view into a folder in the Project view. This creates a prefab. You can now delete the cube from the Hierarchy view.
Update your script to have a public GameObject field, e.g.
public GameObject cubeToCreate;
Then, in the Inspector pane for whatever gameobject has that script attached, you should get a new field, "Cube To Create". Drag your prefab cube into that slot.
Lastly...update your code to wall = Instantiate(cubeToCreate). You'll still need to update the position, but you should be able to drop the rest of the initialization logic you have above, e.g. setting mass and drag.
As for the actual problem, the first thing that concerns me is how do you plan on turning a 2d mouse click into a 3d point? For the axis going "into" the screen...how should the game determine the value for that?
Camera.main.ScreenToWorldPoint accepts a Vector3, but you're passing it a Vector2 (Input.mousePosition, which gets converted to a z=0 Vector3), so the point is 0 units from the camera -- so in a plane that intersects with the camera.
I haven't done this, but I think you'll need to do raycasting of some sort. Maybe create an invisible 2d plane with a collider on it, and cast a physics ray from the camera, and wherever it hits that plane, that's the point where you want to create your cube. This post has a couple hints, though it's geared toward 2D. That might all be overkill, too -- if you create a new Vector3 and initialize it with your mouse position, you can set the z coordinate to whatever you want, but then your cube creation will be in terms of distance from the camera, which is not the best idea.
Hope this helps.

How to place objects when clicking onto mesh Unity

So I have this mesh that I generate using perlin noise
What I want to do is to be able to click to place an object in the game. My ultimate goal is to have a menu so you can place different objects but I want to just try to get a cube to work. I tried RayCasting:
public class Raycast : MonoBehaviour
{
Ray myRay; // initializing the ray
RaycastHit hit; // initializing the raycasthit
public GameObject objectToinstantiate;
// Update is called once per frame
void Update()
{
myRay = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(myRay, out hit))
{
if (Input.GetMouseButtonDown(0))
{
Instantiate(objectToinstantiate, hit.point, Quaternion.identity);
Debug.Log(hit.point);
}
}
I could not get this to work... I had no real idea how to use this script either (like what object to place it on) I tried just making a cube and putting the script on that but It did not work. I got no errors it just did not work.
I also had a mesh collider on my mesh and a box collider on my cube.
I see that you are following the tutorial on YouTube "Procedural Landmass Generation", which I would say is an excellent way to understand the basics of procedural content generation.
For your question I would suggest you add a camera on the top of the terrain, from where you will be able to point with your mouse where you want to instantiate the object (the cube for example). You would need a function which raycasts to the your land tiles. Check if its gameobject's tag is "Terrain" and check if there is no water at that current location. If terrain check returns true and water check returns false, then instantiate your desired object at the "hit" position of the raycast.
You can put the script on any object you want. I'd propose the camera. After adding this script-component to the camera, drag your mesh (that you want to instantiate) into the "objectToinstantiate" slot you see in the script.
Then see if it works, and for performance move the if (Input.GetMouseButtonDown(0)) up in your code, before you do the raycast, like #Basile said.
Note: This script will work in play-mode on the game-view - not in the editor scene-view.
If you'd like this to work too, you should add [ExecuteInEditMode] above the public class ... line. But be careful with those, it's sometimes hard to stop those scripts :P

Unity3d flappy bird tutorial - space button not working

I am a total beginner at Unity3d. I have some background in android programming, but no C# experience whatsoever. The first thing I am trying to do is to create a clone of flappy bird game, called flappy plane, according to this tutorial
http://anwell.me/articles/unity3d-flappy-bird/
The problem is, when I tried to write a script that allows player to move (player.cs) with the code
using UnityEngine;
using System.Collections;
public class player: MonoBehaviour {
public Vector2 jumpForce = new Vector2(0,300);
public Vector2 jumpForce2 = new Vector2(0,-300);
// Use this for initialization
// Update is called once per frame
void Update () {
if (Input.GetKeyUp("space")){
Rigidbody2D.velocity = Vector2.zero;
Rigidbody2D.AddForce(jumpForce);
}
}
}
I get an error "An Object reference is required to access non-static member 'UnityEngine.Rigidbody2D.velocity'". I have googled that and it is suggested to access Rigidbody2d with GetComponent().velocity,
so I changed
Rigidbody2D.velocity = Vector2.zero;
Rigidbody2D.AddForce(jumpForce);
with
GetComponent<Rigidbody2D>().velocity = Vector2.zero;
GetComponent<Rigidbody2D>().AddForce(jumpForce);
The error is gone and I am able to add the script to the object, still I don`t get the desired action - after I hit play the object turns invisible and just falls down, does not react to spacebar button. What am I doing wrong?
Thanks for the answer.
It's possible that you're not adding enough force to have the object move upwards.
There's technically nothing wrong with your code. (Although you do have somethings mixed up in your question). The problem is in the fact that you're not adding ANY force upwards every single frame.
Essentially, at the moment, your player object is in free-fall the instant you hit the play button, and you're adding a minuscule force to the player only on the frames that the space bar is pressed.
To solve this, here's what you should be doing
Add an upward force to counter-act the force of gravity every frame. You can do this in two ways.
a. Set the rigidbody's velocity.y to 0 BEFORE detecting the space bar (this is really a hacky way, but it'll suffice and doesn't need any more code)
b. Add an upward force to the player which will nullify the effect of gravity. Just use F = mg to get the value of force you'd need to add.
You could, alternatively set the isKinematic property to true by default on the Player's rigidbody, set it to false on pressing the space bar, and back to true after a few frames (5 - 6 frames)
make sure your player object and the ground both have BoxCollider2D colliders to keep above ground.
you could keep a reference stored for the rigidBody like Rigidbody2D myRigidbody;
then in start put myRigidbody = GetComponent<Rigidbody2D>(); then you would use like myRigidbody.AddForce(jumpForce); your jumpForce2 though is shooting your player downward you should not need it in a jump as the physics and gravity will apply with the rigidbody.
incase your input is not set up in the project settings try to fire the jump with
Input.GetKeyDown(KeyCode.Space);

Categories