Clamp action distance in isometric grid - c#

I am currently working on a 2D isometric game where the player will be able to control different units, and interact through them.
I created a Scriptable Object called UnitType. Thanks to this system I can define an action range and a moving range, that is the maximum distance in cells that the player can move to or interact with.
The problem is I don't know how to implement this through code. This is what I want to achieve, using an action range of 2 cells for the demonstration.
This is the goal
With a friend of mine, we thought about calculating the linear equation of those 4 lines to check if the raycast hit was within them, but it's not working right with negative x.
This is the current system
What would be the best practice ?
Thank you very much for your time and attention,

Mousehit is a Unity GameObject that is the child of the character. Its positions is bound to the center of the tilemap cells thanks to mousePosition that is a script attached to the GameManager.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class Interact : MonoBehaviour
{
[SerializeField] private Sprite upperLeft, upperRight, midLeft, midRight, bottomLeft, bottomMid, bottomRight;
[SerializeField] private GameObject mouseHit;
public UnitType unitType;
private GameObject gameManager;
private CheckMousePosition mousePosition;
private bool isInteracting, isAtGoodDistance;
private SpriteRenderer spriteRenderer;
private Tilemap tilemap;
private RaycastHit2D hit;
//private Transform mouseHit;
void Start()
{
tilemap = GameObject.Find("Tilemap").GetComponent<Tilemap>();
gameManager = GameObject.FindGameObjectWithTag("GameManager");
mousePosition = gameManager.GetComponent<CheckMousePosition>();
spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
isInteracting = false;
mouseHit.SetActive(false);
}
// Update is called once per frame
void Update()
{
if (isInteracting)
{
mouseHit.SetActive(true);
mouseHit.transform.position = new Vector3(mousePosition.tilemap.GetCellCenterLocal(mousePosition.cellPosition).x, mousePosition.tilemap.GetCellCenterLocal(mousePosition.cellPosition).y, 1);
if (Input.GetMouseButtonDown(0))
{
Interaction();
}
}
if (isInteracting == false)
{
spriteRenderer.sprite = null;
mouseHit.SetActive(false);
}
}
public void InitInteraction()
{
isInteracting = true;
transform.root.GetComponent<ContextualMenu>().CloseContextualMenu();
}
private void Interaction()
{
//When the player interacts, we cast a ray that will determine what he is interacting with.
hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if (hit.collider != null)
{
if (isInteracting)
{
isInteracting = false; //the player is no longer interacting.
//If the player is interacting with a farmable cell using the worker.
if (transform.root.name == "Worker"
&& hit.collider.GetComponent<FarmableCell>() != null)
{
CheckInteractionDistance();
//hit.collider.GetComponent<FarmableCell>().CheckIfFarmed();
}
}
}
}
private void CheckInteractionDistance()
{
if (mouseHit.transform.localPosition.y <= (-0.5 * (mouseHit.transform.localPosition.x) + unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x >= (-0.5 * (mouseHit.transform.localPosition.x) - unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x >= (0.5 * (mouseHit.transform.localPosition.x) - unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x <= (0.5 * (mouseHit.transform.localPosition.x) + unitType.actionDistance / 2))
{
Debug.Log("ok");
}
else
{
Debug.Log("not ok");
}
}
}

Related

rayHit.point Instantiates at 0 0 0 Unity

I made a Gun Script (using YT videos) and a Bullethole GameObject should Instantiate at the Position the RayCast hits an Object. But it is always at 0 0 0. I tried many thing but non work.
GameObject bulletHoleClone = Instantiate(bulletHole, rayHit.point, Quaternion.Euler(0, 180, 0));
Destroy(bulletHoleClone, bulletHoleTime);
Whole Code:
using TMPro;
using UnityEngine;
public class GunScript : MonoBehaviour
{
//Gun Stats
public int damage;
public float timeBetweenShooting, spread, range, reloadTime, timeBetweenShots;
public int magazineSize, bulletsPerTap;
public bool allowHoldButton;
int bulletsLeft, bulletsShot;
private float normalSpread;
//bools
bool shooting, readyToShoot, reloading;
//Graphics
public CameraShake camShake;
public float camShakeStrengh, camShakeDuration;
public GameObject muzzleFlash, bulletHole;
public TextMeshProUGUI text;
public float muzzleFlashTime = 0.1f; //Will be deleted after there is an actual Effect and not a Box
public float bulletHoleTime = 3f;
//Refrences
public Camera fpsCam;
public Transform attackPoint;
private RaycastHit rayHit;
public LayerMask dafuqIsEnemey;
private Rigidbody rb;
private void Awake()
{
rb = GetComponent<Rigidbody>();
bulletsLeft = magazineSize;
readyToShoot = true;
normalSpread = spread;
}
private void Update()
{
MyInput();
//Set Text to BulletAmount
text.SetText(bulletsLeft + " / " + magazineSize);
}
private void MyInput()
{
if (allowHoldButton) shooting = Input.GetKey(KeyCode.Mouse0);
else shooting = Input.GetKeyDown(KeyCode.Mouse0);
if (Input.GetKeyDown(KeyCode.R) && bulletsLeft < magazineSize && !reloading) Reload();
//PewPew
if (readyToShoot && shooting && !reloading && bulletsLeft > 0)
{
bulletsShot = bulletsPerTap;
Shoot();
}
if (rb.velocity.magnitude > 0)
spread = normalSpread * 1.5f;
else spread = normalSpread;
if (Input.GetKey(KeyCode.LeftControl))
spread = normalSpread * 2f;
else spread = normalSpread;
}
private void Shoot()
{
readyToShoot = false;
//Bullets go BRRRRRRRRRR
float x = Random.Range(-spread, spread);
float y = Random.Range(-spread, spread);
//Calculate Direction with Spread
Vector3 direction = fpsCam.transform.forward + new Vector3(x, y, 0);
//Maybe Add that the Ray Isnt coming out of your Head. Instead make it apear on the GunTip
if (Physics.Raycast(fpsCam.transform.position, direction, out rayHit, range, dafuqIsEnemey))
{
Debug.Log(rayHit.collider.name);
if (rayHit.collider.CompareTag("Enemy"))
{
Debug.Log("Hit An Enemy");
//rayHit.collider.GetComponent<ShootingAi>().TakeDamage(damage);
}
}
//Shake Camera
camShake.Shake(camShakeDuration, camShakeStrengh);
//Graphics
GameObject bulletHoleClone = Instantiate(bulletHole, rayHit.point, Quaternion.Euler(0, 180, 0));
GameObject muzzleClone = Instantiate(muzzleFlash, attackPoint.position, Quaternion.identity);
Destroy(muzzleClone, muzzleFlashTime);
Destroy(bulletHoleClone, bulletHoleTime);
Debug.Log(rayHit.point);
bulletsLeft--;
bulletsShot--;
Invoke("ResetShot", timeBetweenShooting);
if (bulletsShot > 0 && bulletsLeft > 0)
Invoke("Shoot", timeBetweenShooting);
}
private void ResetShot()
{
readyToShoot = true;
}
private void Reload()
{
reloading = true;
Invoke("ReloadFinished", reloadTime);
}
private void ReloadFinished()
{
bulletsLeft = magazineSize;
reloading = false;
}
}
You're not hitting anything. You check to see if you hit something, and then spawn a bullet hole even if you didn't hit anything.
If you didn't hit anything, there isn't anywhere to put a bullet hole.
The problem is that you're using the raycast hit as a local variable, which allows you to use it outside of the scope where it's actually valid.
Delete rayHit from your class and define it in the raycast instead, like
out RaycastHit rayHit
then it should only be scoped for the if statement and you'll get a compiler error anywhere it's not valid.
This brings up a larger question about what do you do if it misses an enemy, or which layers you should be raycasting against. Personally I think you'd want to raycast against everything, then you can try to determine what you hit and what that means later. Only other really good alternative would be to raycast against enemies, then repeat that against objects if that misses, but it's a lot of overhead to keep spinning up the raycasting engine. You'd be better off firing against multiple layers and getting the layer off the collider.
PS too your spread is probably way, way too big. For values of spread, 0.01 is half a degree, 0.1 is five degrees, 1 is 45 degrees.
I found the Problem. Just tell the Gun that the Layer "Ground" is also an Enemy. Now it works. Thanks
Basically you need to Transform Direction e.g camTrans.TransformDirection. You can try this way
public class Raytest : MonoBehaviour
{
public Camera fpsCam;
public float maxDistance = 200f;
public bool update = true;
public GameObject objectToSpawn;
private void Update()
{
if(!update) return;
var camTrans = fpsCam.transform;
if (Physics.Raycast(camTrans.position, camTrans.TransformDirection(Vector3.forward), out var hit, maxDistance))
{
Debug.DrawRay(camTrans.position, camTrans.TransformDirection(Vector3.forward) * hit.distance, Color.red);
Debug.Log("Did Hit");
var clone = Instantiate(objectToSpawn, hit.point, Quaternion.Euler(0, 180, 0));
update = false;
}
else
{
Debug.DrawRay(camTrans.position, camTrans.TransformDirection(Vector3.forward) * 1000, Color.white);
Debug.Log("Did not Hit");
}
}
}
After hitting raycast

unity Transform position setting boundries in object

I am trying to make a movement where sprites go down in a row inside a panel, and then they stop when reaching the bottom boundry of the panel. Problem is after i set the position of the objects where it has to stop moving, its taking me position.y = 0 as if it was the middle of the screen. How do i set it to stop moving, when reaching the bottom of its parent object (the panel). Thank you.
here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Transform_test : MonoBehaviour
{
[SerializeField]
SpriteRenderer[] Reels;
[SerializeField]
Transform[] ReelsTransform;
int speed;
bool drop;
[SerializeField]
GameObject Panel;
void Start()
{
Debug.Log(transform);
//transform.position = new Vector3 (10,0,0);
speed = 1;
drop = false;
}
// Update is called once per frame
void Update()
{
// test();
if (drop == false)
{
foreach (Transform Reels in ReelsTransform)//This Targets All Children Objects Of The Main Parent Object
{
Debug.Log("pos " + Reels.transform.position.y);
for (int i = 0;i<5;i++)
{
//Direction And Speed Of Movement
new WaitForSeconds(Random.Range(1, 3));
Reels.transform.Translate(Vector3.down * Time.smoothDeltaTime * speed, Space.World);
if (Reels.transform.position.y <= 0 )
{
Reels.transform.position = new Vector3(Reels.transform.position.x, Reels.transform.position.y + 58, Reels.transform.position.z);
}
}
}
}
}
}
Ok this is how i solved it. I created an empty sprite on the place where i want the reel to stop, then i set the if to match that position with the position of the first sprite in the reel. If the positions match. Turn moveto to true and stop the movement
public class transform2 : MonoBehaviour
{
public float speed;
public SpriteRenderer sprite1;
public SpriteRenderer[] sprites;
public SpriteRenderer cell1;
public GameObject Panel;
bool moveto;
void move()
{
if (moveto == false)
{
foreach (SpriteRenderer sprites in sprites)
{
sprites.transform.Translate(Vector3.down * Time.smoothDeltaTime * speed, Space.World);
if (sprite1.transform.position.y <= cell1.transform.position.y)
{
moveto = true;
sprite1.transform.position = cell1.transform.position;
}
Debug.Log("MOVE");
}
}
}
// Start is called before the first frame update
void Start()
{
speed = 20;
}
// Update is called once per frame
void Update()
{
move();
}
}

Unity2D Grab Object Script

I'm trying to get this grabbing script working with my game. In The game you play as a fish in water, and fish should be able to grab and carry objects.
Problem:
I get that with raycast you fire The ray to certain direction which doesn't work for me in this game. This code is otherwise working fine, but I can't figure how to fix the code so that objects can be grabbed from any direction as long as in range.
What I've tried:
I've tried Physics2D.CircleCast, but there also you have to set the certain direction for the cast(?) Also tried OnTriggerEnter but couldn't get that working either. Is there way to cast ray to all directions(not z)? Or is there some more simple way to do this?
using UnityEngine;
using System.Collections;
public class grabberscript : MonoBehaviour
{
public bool grabbed;
RaycastHit2D hit;
public float distance = 0f;
public Transform holdpoint;
public LayerMask notgrabbed;
void Update()
{
if (Input.GetButtonDown("Fire3"))
{
if (!grabbed)
{
Physics2D.queriesStartInColliders = false;
hit = Physics2D.Raycast(transform.position, Vector2.right * transform.localScale.x, distance);
if (hit.collider != null && hit.collider.tag == "grabbable")
{
grabbed = true;
}
}
else
{
grabbed = false;
}
}
if (grabbed)
hit.collider.gameObject.transform.position = holdpoint.position;
}
}
What you are looking for is Physics2D.OverlapCircle .. instead of casting the circle into a certain direction this only checks whether a collider lies within a circle at given center and radius. You the should not check via a tag but rather use a dedicated layer for all objects that shall be able to be grabbed
public class grabberscript : MonoBehaviour
{
public float distance = 0f;
public Transform holdpoint;
// Use the layer(s) for all objects that shall be grab able
public LayerMask grabbable;
private Transform currentlyGrabbedObject;
void Update()
{
if (Input.GetButtonDown("Fire3"))
{
if (!currentlyGrabbedObject)
{
Collider2D hit = Physics2D.OverlapCircle(transform.position, distance, grabbable);
if (hit)
{
currentlyGrabbedObject = hit.transform;
}
}
else // release currently grabbed object
{
currentlyGrabbedObject = null;
}
}
if (currentlyGrabbedObject)
{
currentlyGrabbedObject.position = holdpoint.position;
}
}
}
If you don't use a Layer as said before you would rather have to use Physics2D.OverlapCircleAll and use the first hit matching your tag e.g. using Linq FirstOrDefault like
using System.Linq;
...
Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, distance);
Collider2D hit = hits.FirstOrDefault(h => h.CompareTag("grabbale");
if (hit)
{
currentlyGrabbedObject = hit.transform;
}
I think you can use an OverlapCircle to check whether the item to grab is in range or not like this:
if(!grabbing)
{
//This will save the position of all gameObjects in a specific range which are set in a or multiple specific layers
Collider2D[] grabbedItems = Physics2D.OverlapCircleAll(transform.position, grabRange, grabbableLayer);
}
Foreach(Collider2D item in grabbedItems)
{
if(item.ComapreTag("grabbable"))
{
item.isGrabbed = true;
}
}
Then in the gameObject that you intend to make grabbable, you can add a script like this:
public bool isGrabbed = false;
public Transform holdPoint;
void Update()
{
if(isGrabbed)
{
TrackHoldPoint();
}
}
private void TrackHoldPoint()
{
transform.position = holdPoint.position;
}
Then just to be easier to set, you can add this small script to see in edit mode the range of the cirlce:
private void OnDrawGizmosSelected()
{
if (transform == null) return;
Gizmos.DrawWireSphere(transform.position, range);
}

How do I change a GameObject's rotation equal to my player camera's rotation?

Im working on a first person shooter. I have an aim function, which puts the pistol right in front of the camera, to make it look like your holding it in front of you. Im trying to make it so the pistol will also rotate with the camera on the Z axis, so that way the pistol wont stay still, because that looks odd and gets in the way. To do this, I tried this:
GPR.gun.transform.rotation = Quaternion.Euler(0, 0, plrCam.transform.rotation.z);, however this ends up rotating the gun very slightly around the z axis, and mainly rotating it around the y axis whenever I move my camera. I am a beginner programmer in Unity so please try to make answers more digestible to beginners so I can understand it. Here is my full script:
using System.Collections.Generic;
using UnityEngine;
public class PistolFire : MonoBehaviour
{
//Gun Properties
public float range = 50f;
public float damage = 10f;
//Sensitivity decrease for looking down the sights
public float downSights = 5f;
//Other vars
private playerGunControls playerGun;
private GameObject plrCam;
private Camera fpsCam;
private ParticleSystem muzzleFlash;
private GameObject impactEffect;
private bool aimed = false;
private GameObject aimPos;
private GunPickupRaycast GPR;
private GameObject handPos;
private GameObject Player;
// Start is called before the first frame update
void Start()
{
//Getting objects because gun is instantiated, so this is necessary
plrCam = GameObject.Find("Player Camera");
playerGun = plrCam.GetComponent<playerGunControls>();
fpsCam = plrCam.GetComponent<Camera>();
muzzleFlash = GetComponentInChildren<ParticleSystem>();
impactEffect = GameObject.Find("Impact Effect");
aimPos = GameObject.Find("aimPos");
GPR = plrCam.GetComponent<GunPickupRaycast>();
handPos = GameObject.Find("Hand Pos");
Player = GameObject.Find("Player");
}
// Update is called once per frame
void Update()
{
//Check for shoot button down
if (Input.GetButtonDown("Fire1"))
{
if (playerGun.holding == "Pistol")
{
Shoot();
}
}
//Check if aim button down
if (Input.GetButton("Fire2"))
{
if (playerGun.holding == "Pistol")
{
Aim();
}
}
//Check if no longer aiming to reset to normal
if (aimed == true && !(Input.GetButton("Fire2")))
{
Unaim();
}
}
void Shoot()
{
muzzleFlash.Play();
RaycastHit hit;
if(Physics.Raycast(plrCam.transform.position, plrCam.transform.forward, out hit, range))
{
Debug.Log(hit.transform.name);
Health health = hit.transform.GetComponent<Health>();
if (health != null)
{
health.TakeDamage(damage);
}
//Instantiate the Impact Effect
GameObject IE = Instantiate(impactEffect, hit.point, Quaternion.identity);
Destroy(IE, 1.5f);
}
}
void Aim()
{
aimed = true;
Debug.Log("Aiming");
GPR.gun.transform.position = aimPos.transform.position;
GPR.gun.transform.rotation = Quaternion.Euler(0, 0, plrCam.transform.rotation.z);
}
void Unaim()
{
GPR.gun.transform.position = handPos.transform.position;
Debug.Log("No longer aiming");
aimed = false;
}
}
I fixed my problem by making the gun a child of my camera instead of a child of my player.

Camera Follow for 2D - GameObjects Blink Out Upon Jump

Yes, I read through about 30 different similar titles before posting this. However, there wasn't anything relevant to what I need. I'm trying to set my camera's Y-axis to follow the player as it moves through the level; however, I don't want the camera to move up and down whilst jumping so I follow the camera's transform.position.y and NOT the player's.
void Pjump()
{
if (Input.GetKeyDown(KeyCode.Space) && onFloor==true
|| Input.GetKeyDown(KeyCode.W) && onFloor==true)
{
player.velocity = new Vector2(0, jump);
onFloor = false;
isJumping = true; // Static Variable to pass onto CamFollow script
}
}
isJumping is set false inside an OnCollisionEnter2d() and called inside of a FixedUpdate().
Now for the CamFollow script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CamFollow : MonoBehaviour
{
private GameObject p1;
private bool isFollow;
[Header("Camera Offset Values")]
[SerializeField]
private float xOff;
[SerializeField]
private float yOff;
void Start()
{
p1 = GameObject.FindWithTag("Player");
isFollow = true;
}
void FixedUpdate()
{
if (isFollow)
{
if (Pmove.isJumping == false) // This code works fine
{
transform.position = new Vector3(p1.transform.position.x + xOff, p1.transform.position.y + yOff,
transform.position.z);
}
if(Pmove.isJumping == true) // This is where the problem is: Y-Axis
{
transform.position = new Vector3(p1.transform.position.x + xOff, transform.position.y + yOff,
transform.position.z);
}
}
}
}
When the player jumps, the player and all non-UI objects vanish until the player touches the ground.

Categories

Resources