I am trying to make a game which allows moving objects by snapping them on the grid, I already figured out to snape them to grid but there is one thing little problem, I want to check if there is already a game object placed on that same grid so that I won't let the dragging game object snap to that same spot but the thing is that I have a different game object shapes.
see for yourself
Click to see the image
how can I achieve that?
Since you're on a square grid I think the best way to do this is with Physics2D.Boxcast(). Basically what you're doing is casting a box at the snap vector before moving the game object.
So in your code before you move the game object to the snap location:
RaycastHit2D hit = Physics2D.BoxCast(snapTo, boxSize, 0.0f, Vector2.zero);
if (hit == null)
{
// We're clear to move
}
else
{
// Something is in the way
}
Where snapTo is the Vector2 of the location you're going to snap to and boxSize is a Vector2 equal to the size of one grid position (you might need to play around with this a bit). The last two arguments, 0.0f refers to the angle of the box, which we don't need so just set it to zero and Vector2.zero is the direction of the cast, but we're casting in one spot so this also doesn't matter.
I'm assuming that only one game object can occupy the space at once, so there will only ever be one hit. If there's a chance for more than one you can change it to RaycastHit2D[] hits and Physics2D.BoxCastAll then check if hits.Length is greater than 0.
I had some troubles with Physics2D.Boxcast() , so instead i used Physics2D.OverlapBox() and it is working just fine.
isColl = Physics2D.OverlapBox(ObjectToMove.position, size, 0f, layerM);
if (isColl == true)
{
// Something is in the way
}
else
{
//Clear to go
}
Related
I'm coding a top down game, with point and click movement. Currently you are able to click on the map, but you can also click outside the map to move there. I added colliders to the walls, but you still try and go outside. Code example:
if (Input.GetMouseButtonDown(1)) {'move'}
But what I want is something like this:
if (Input.GetMouseButtonDown(1) on MAP) //map is the object
So I want to be able to only click on the map, and if you click outside the map, it won't do anything. Thanks!
My script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed = 10f;
Vector2 lastClickedPos;
//Rect inRect = new Rect(82.80f, -83.20f, 164.90f, 163.29f);
bool moving;
private void Update()
{
if (Input.GetMouseButtonDown(1) && GameObject.CompareTag("clickedOn")){ // && inRect.Contains(Input.mousePosition)
lastClickedPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
moving = true;
}
if (moving && (Vector2)transform.position != lastClickedPos)
{
float step = speed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, lastClickedPos, step);
}
else
{
moving = false;
}
}
}
I think you can resolve your issue using tags or layers. I'll just list how to setup tags since it has a lot less setup vs. doing layers.
First off you'll need to create a tag and since Unity has good documentation on stuff like this I'll just link it here: https://docs.unity3d.com/Manual/Tags.html
Once you created your tag and tagged the ground/environment/area you want to have be clickable with the tag then you just need to find the object you want to collided with and use CompareTag so to put that in an example here is what your if statement could look like:
if (Input.GetMouseButtonDown(1) && collidedObject.CompareTag("TagNameGoesHere"))
{
//Movement goes here
}
Just to note I've named the gameObject that was found to collidedObject but you can name it whatever you want.
Hopefully this helps, let me know if I need to clarify something, it has been awhile since I've done a stackoverflow answer so I may have left something out.
Edit: Alright so adding onto this, you'll need to also look into how to do raycasting to check what object you click on so you can determine if it's a spot you can move to.
I've just tested this in a project just to make sure I understand it (I've used raycast a lot but never really done point to click movement before).
Essentially I've broken down things into 3 statements, which you can add together into one if statement but it's more so I can explain everything in detail:
if (Input.GetMouseButtonDown(1))
What you use currently, we want to make sure we only do the next few checks when we click
if (Physics.Raycast(playerCamera.ScreenPointToRay(Input.mousePosition), out hit, Mathf.Infinity))
So this here, is essentially drawing a line from a position, in this bit we are using the player camera which is just a regular camera reference and converting a point on screen into a ray which we then set the length to be Mathf.Infinity (this can be whatever float, I've just used this for an example) and then we output the hit object to hit which is a RaycastHit struct.
if (hit.collider != null && hit.collider.CompareTag("TagNameGoesHere"))
Now we finally check to see if the collider is not null (in case we hit the sky or something, which shouldn't happen for you in a top down game) and also that the object has right tag. Again you need to setup the tags which I've listed above and make sure you set the correct game objects in scene to have the correct tag. After which you should be able to move to the position (using the position you've setup and such is fine)
So it should look something like this:
if (Input.GetMouseButtonDown(1))
{
if (Physics.Raycast(playerCamera.ScreenPointToRay(Input.mousePosition), out rayCastHit, Mathf.Infinity))
{
if (hit.collider != null && hit.collider.CompareTag("TagNameGoesHere"))
{
//Movement goes here
}
}
}
Make sure to also put a reference to the camera and RayCastHit objects (these are the playerCamera and rayCastHit variables in the above). For my example script that I created I made them global variables.
I have made a gameobject together with some children gameobject to represent the information to show up when specific circumstances occurred.
I have already ajusted the position of the information gameobject(together with its' children) in the cameraarea. The thing is that I want to move the gameobject(together with its' children) out of the camera, maybe on top or maybe on left. Following is the scratch to demonstrate the position I want to put it:
So that I could move the information gameobject and its' children (Red box) with some movement effect when needed, I have no problem with moving it back but could find an elegant way to move it out of the camera when the game started.
Mostly because I don't know how to calculate the position out of the camera.
Maybe find the upper border of the camera and the size of the gameobject and its children?
I know I could do this by maybe add a marker gameobject to represent the downer border of the information gameobject, and move it until it's not visible, but is there a more elegant way?
Any ideas?
For this one, I would use the following trick: use any method (animation, coroutine, Update method...) to move your item out of screen the way you desire. Then you can use the OnBecameInvisible event which is called when the item does not need to be rendered on any camera anymore. The event will there be used to detect that the parent object moved out of screen, and that you want to stop the current movement. You then just need to define in this event that you want to stop the current moving behavior, and you will be done.
void OnBecameInvisible() {
// Stop moving coroutine, moving in Update or current animation.
}
There are probably more elegant ways of doing it as you said, but I think this method should be enough for what you want to achieve.
It took me time, but I found this way for you, attach this script to your gameObject:
public Renderer rend;
//drag the camera to the script in the inspector
public Camera camera1;
Vector3 bottomleft;
Vector3 topright;
void Start()
{
rend = GetComponent<Renderer>();
//the top-right point of the camera bounds
topright= camera1.ViewportToWorldPoint(new Vector3(0, 0, transform.position.z));
//the bottom-left point of the camera bounds
bottomleft = camera1.ViewportToWorldPoint(new Vector3(1, 1, transform.position.z));
StartCoroutine(MoveUp());
}
IEnumerator MoveUp()
{
//while the position and the height are lower that the Y of top right
while (transform.position.y + rend.bounds.size.y < topright.y)
{
//move the object to the new position (move it up)
transform.position = new Vector3(transform.position.x, transform.position.y + .01f, transform.position.z);
//and wait for 1/100 of a second
yield return new WaitForSecondsRealtime(.001f);
}
}
you can play with the WaitForSecondsRealtime value to change the velocity of moving.
I've created an arm with a custom pivot in Unity which is essentially supposed to point wherever the mouse is pointing, regardless of the orientation of the player. Now, this arm looks weird when pointed to the side opposite the one it was drawn at, so I use SpriteRenderer.flipY = true to flip the sprite and make it look normal. I also have a weapon at the end of the arm, which is mostly fine as well. Now the problem is that I have a "FirePoint" at the end of the barrel of the weapon, and when the sprite gets flipped the position of it doesn't change, which affects particles and shooting position. Essentially, all that has to happen is that the Y position of the FirePoint needs to become negative, but Unity seems to think that I want the position change to be global, whereas I just want it to be local so that it can work with whatever rotation the arm is at. I've attempted this:
if (rotZ > 40 || rotZ < -40) {
rend.flipY = true;
firePoint.position = new Vector3(firePoint.position.x, firePoint.position.y * -1, firePoint.position.z);
} else {
rend.flipY = false;
firePoint.position = new Vector3(firePoint.position.x, firePoint.position.y * -1, firePoint.position.z);
}
But this works on a global basis rather than the local one that I need. Any help would be much appreciated, and I hope that I've provided enough information for this to reach a conclusive result. Please notify me should you need anything more. Thank you in advance, and have a nice day!
You can use RotateAround() to get desired behaviour instead of flipping stuff around. Here is sample code:
public class ExampleClass : MonoBehaviour
{
public Transform pivotTransform; // need to assign in inspector
void Update()
{
transform.RotateAround(pivotTransform.position, Vector3.up, 20 * Time.deltaTime);
}
}
I'm playing around with an FPS where I want my player(s) to be able to build/construct their own buildings from scratch. I've searched around for exisiting solutions/theories, but have so far been unable to find anything suitable to my needs. Please point me in the right direction if I've missed anything.
Where I am right now is that I have three prefabs; floor, wall and wall with a door opening. First I want to instantiate floor tiles which I then can put walls on, and hopefully being able to have the walls snap to the edges/corners of the floor tiles.
Can anyone please point me in the right direction for how to do this? Also, does my desired "work flow" at all make sense? Any pitfalls in there?
Thanks in advance!
UPDATE: Here's what I have in regards to instantiation prefabs, and while this works (except it's like I'm shooting walls), I would like the wall to snap to the corners/edges of the nearest floor (which has already been instantiated in the same fashion.
[RequireComponent (typeof (CharacterController))]
public class PlayerController : MonoBehaviour {
// Declare prefabs here
GameObject wallPrefab;
// Initialise variables before the game starts
void Awake () {
wallPrefab = (GameObject)Resources.Load( "WoodWall" );
}
// This happens every frame
void Update () {
if ( Input.GetButtonDown("Fire1") ) {
// Instantiate new wall
Instantiate( wallPrefab, cc.transform.position + cc.transform.forward + Vector3.up * 1.0f, wallPrefab.transform.rotation );
}
}
}
hmm... well one solution I can think of, is to have the wall raycast downwards in order to find a floor, then move to a predetermined position in relation to that floor (if it found any). Stick this in a script in the wall prefabs:
void Start()
{
var down = transform.TransformDirection (Vector3.down); //down might not actually be the down direction of your object, check to make sure
RaycastHit hit;
if (Physics.Raycast(transform.position, down, out hit) && hit.collider.gameObject.name == "myFloorName") //Maybe use tags here instead of name
{
Vector3 floorPos = hit.collider.gameObject.transform.position;
Vector3 floorSize = hit.collider.gameObject.transform.localScale;
this.transform.position = new Vector3(floorPos.x - floorSize.x/2, floorPos.y - this.tranform.localScale.y/2, floorPos.z); //These might need fiddling with to get right
}
}
void Update()
{
}
Vector3.down may not correspond to the down direction for the wall, since this can depend on the 3d model too, so you might need to fiddle with that. The position might also need fiddling with (this assumes that y corresponds to height, which might not be the case), but hopefully this gives you a rough idea of how it can be done. Also, if you don't know what the name of the floor object is, you could probably check by tags, which is probably easier.
If anything else needs clarifying, leave a comment and I'll get back to you
I have an issue with trying to get an 'object(character)' to walk around a cube (all sides) within Unity. Ive attached an image and video showing what i am trying to achieve. Im trying to show you visually rather than trying to explain. As the character drops over the edge it rotates 90 degrees and then the stands up like gravity has switched. Then the character can jump walk etc.
This is an example of someone else that posted a video showing exactly what im trying to achieve
I have looked through the forums and cant find what im after. i have tried to attach a diagram but the site wont let me. Any advice would be greatly appreciated!
Regards
Nick
You have a couple of options that I can think of.
One is to trigger the gravity change when the character exits one face of the cube to go to another. To achieve this you would have trigger zones on each edge and face and use a [Bob went from Face A to Edge ANorth -> Switch Gravity to go in X direction].
This would work for situations where the gravity switch must affect other objects too but be advantageous to your player (walking off the side makes an enemy fall off and die - for example.)
However, if you want all entities to stick to their relative sides then we need to make custom gravity! To do this is easier than you might think as gravity is simply a downward accelleration of 9.8. So turn off the engines native gravity and create a "personal gravity" component:
private Vector3 surfaceNormal;
private Vector3 personalNormal;
private float personalGravity = 9.8f;
private float normalSwitchRange = 5.0f;
public void Start()
{
personalNormal = transform.up; // Start with "normal" normal
rigidbody.freezeRotation = true; // turn off physics rotation
}
public void FixedUpdate()
{
// Apply force taking into account character normal as personal gravity:
rigidbody.AddForce(-personalGravity * rigidbody.mass * personalNormal);
}
Rotating your character and changing his normal is then up to you to suit your situation or game mechanic, whether you do that by raycasting if you're standing on a surface to detect when to change it or only want gravity to change when you hit a powerup or similar - experiment and see what works. Comment more if you have questions!
EDIT:
As an addition to the video you linked. You can keep a state variable on the jump state and raycast in each axis direction to check which face is nearest in the case of just rolling off.
public void Update()
{
// we don't update personal normals in the case of jumping
if(!jumping)
{
UpdatePersonalNormal();
}
}
public void UpdatePersonalNormal()
{
RaycastHit hit; //hit register
// list of valid normals to check (all 6 axis)
Ray[] rays =
{
Vector3.up, Vector3.down,
Vector3.left, Vector3.right,
Vector3.forward, Vector3.backward
};
//for each valid normal...
foreach(Ray rayDirection in rays)
{
//check if we are near a cube face...
if(Physics.Raycast(rayDirection , hit, normalSwitchRange)
{
personalNormal = hit.Normal; //set personal normal ...
return; // and return as we are done
}
}
}
Please keep in mind that the above is completely hand written and not tested but play with it and this pseudo start should give you a good idea of what to do.