I'm trying to make a top-down game similar to Clash and TopWar with the merge mechanics. At the moment I have the ability to merge using the code below.
private void OnTriggerEnter(Collider collision)
{
Debug.Log("Colliding With: " + collision.transform.name);
colliding = true;
if (collision.transform.gameObject.GetComponent<SoldierController>() == null)
{
return;
}
if (collision.transform.gameObject.GetComponent<SoldierController>().soldierLevel == soldierLevel)
{
canMerge = true;
}
}
private void OnMouseUp()
{
if(colliding)
{
if (canMerge) isMerge(collisionObject);
else
{
//Outline red.
GetComponent<Outline>().enabled = true;
GetComponent<Outline>().OutlineColor = Color.red;
}
}
}
Then OnMouseUp() resets the local variables for another object to be dragged. The question I have now is that in games like Evermerge and TopWar, when you merge one object with another if there are items of the same tier you can merge as well then you can merge them all at once. I added a screenshot for reference.
I'm building it in 3D and I think my approach with the colliders might be wrong. I've thought about maybe trying to find similar objects from the perspective of the grid to see if there are any objects on the grid that are 5 up or 5 across, but I'm not sure how to use the Grid object like this other than using it as a reference to snap objects into place when being dragged. Let me know if you need any other details. Thanks in advance!
Related
I have 2 Targets in the world that I want to add to a list when the Player comes close to them.
I first use the Physics OverlapBox method to return an array of colliders. After this, I run a for loop in which the 2 targets should get added to the list. Only 2 target objects are in the scene but the list gets occupied with hundreds of copies of those objects.
Code Down below
private void TrySelectTarget(bool switchInput)
{
targetArray = Physics.OverlapBox(transform.position, range, Quaternion.identity, targetLayer, QueryTriggerInteraction.Ignore);
for (int i = 0; i < targetArray.Length ; i++)
{
if (targetArray[i].TryGetComponent<Target>(out Target target))
{
availableTargets.Add(target);
}
}
}
I did a deblug.Log on targetarray.Length and it returned 2, so I don't understand why so many objects are being added to the availableTargets List.
I am calling the TrySelectTarget() method in Update().
I am new to c# and programming, so apologies if I am making a stupid mistake.
Thank you for the help.
With your code everytime Physics.OverlapBox returns hits you add the same objects that have already references stored in the list, again. To simply solve the issue of not having duplicates being stored you should check if an object is already referenced (has an entry in the list). Do that by doing:
if (targetArray[i].TryGetComponent<Target>(out Target target))
{
if (!availableTargets.Contains(target)) availableTargets.Add(target);
}
That will not solve the issue of targets not being removed when not in range anymore though. If that is needed then you should change your code so that the list gets cleared before any new references are being added. You could do:
availableTargets.Clear();
targetArray = Physics.OverlapBox(transform.position, range, Quaternion.identity, targetLayer, QueryTriggerInteraction.Ignore);
for (int i = 0; i < targetArray.Length ; i++)
...
The better solution to solve for this problem in general is to make use of OnTriggerEnter() and OnTriggerExit() messages provided through a Rigidbody component. Those Methods only get invoked if at least one of the interacting objects has a Rigidbody component. Add a Rigidbody to your player object and a collider with the size of the detection range/zone size and set this collider to be IsTrigger. If you dont want that physics affects the object just check the IsKinematic option on the Rigidbody component. In a script on the player then do:
private void OnTriggerEnter (Collider other)
{
if (other.TryGetComponent<Target>(out Target target))
{
// check if not present already to be 100% sure not to get duplicates
// even though it generally shouldn't be happening, better safe than sorry
if (!availableTargets.Contains(target))
{
availableTargets.Add(target);
}
}
}
private void OnTriggerExit (Collider other)
{
if (other.TryGetComponent<Target>(out Target target))
{
availableTargets.Remove(target);
}
}
I think you either want to
availableTargets.Clear();
first so you start with an empty list every time, as anyway you seem to only be interested in the targets overlapping in that instance.
Or you could use Link and do e.g.
using System.Linq;
...
private void TrySelectTarget(bool switchInput)
{
availableTargets = Physics.OverlapBox(transform.position, range, Quaternion.identity, targetLayer, QueryTriggerInteraction.Ignore)
.Select(col => col.GetComponent<Target>())
.Where(target => target)
.ToList();
}
I am trying to make a list of gameObjects disappear when the player enters a room, the best way I could think about doing it, was changing the material alpha frame by frame. If there is an alternative to this method that is more performatic, please tell! (not that I noticed any bad performance as this seems simple enough)
I expect the player to enter a room and all of the gameObjects in that list to disappear at the same time and at the same rate (as if they were all just one object having their transparency changed in runtime) but what happens is (I've made a video: https://youtu.be/z1pz2Te_hDg ) that some gameObjects change before others, some disappear way faster than expected and just in the end after the others, it all looks weird.
Changing the alpha of only one object at a time seems fine, but since I need to loop through all of the materials, I can't simply put all objects as a child of one gameObject.
I've tried to change the alpha without making a custom shader with an alpha property by accessing the default alpha in the default "HDPR/Lit" shader and it behaves the same.
This problem seems easy, I feel like I am doing something stupidly wrong but I've been at this for a couple of weeks.
Here is my "RoomTransparencyController" script:
bool transparent = false;
public float enhance = 5;
private struct ShaderPropertyIDs {
public int AlphaRef;
}
private ShaderPropertyIDs shaderProps;
public List<GameObject> gameObjectList;
public List<Material> materialList;
void Start() {
foreach(GameObject obj in gameObjectList) {
foreach(Material mat in obj.GetComponent<MeshRenderer>().materials) {
materialList.Add(mat);
}
}
// Cache property IDs
shaderProps = new ShaderPropertyIDs() {
AlphaRef = Shader.PropertyToID("AlphaRef"),
};
}
void Update() {
UpdateChildsAlpha3(transparent);
}
private void OnTriggerEnter(Collider col) {
if(col.tag == "Player") {
transparent = true;
}
}
private void OnTriggerExit(Collider col) {
if(col.tag == "Player") {
transparent = false;
}
}
void UpdateChildsAlpha3(bool transparent) {
foreach(Material mat in materialList) {
mat.SetFloat("AlphaRef", Mathf.Clamp(mat.GetFloat("AlphaRef") + Time.deltaTime * enhance * (transparent ? -1 : 1), 0, 1));
}
}
PS: This script should mainly contain props and specific walls from a room so there is a script in every room with its gameObject list for better organization. So I thought that each room having its script was the best way to organize and replicate.
If you want the objects to be gone, then use a for loop with Destroy() on all the objects, and if you want them to stop being visible, just use SetActive(). If you want them to work, but just stop being visible, you can disable the renderer component.
It will be much better if you try to use some tweens to solve this problem. Like doTween or LeanTween, where they have some better-optimized way to reduce alpha or anything. Try to play around with it.
I'm kind of new to Unity. As by the title, I am having trouble getting the collisions right in my game. I am using the custom physics script from unity: https://unity3d.com/learn/tutorials/topics/2d-game-creation/scripting-gravity?playlist=17093. In my game, I am experiencing difficulties in disable collisions.
For example, when I use
Physics2D.IgnoreLayerCollision (8, 9);
It doesn't change anything and the two characters still collide. Also, for some reasons, the triggers behave strange and are still affected by collisions. Going near a character with triggers will make him float up. I've been stuck on this for a long time and I'd really appreciate the help.
It is worth noting that I am using custom physics and using a box collider 2d to detect attack ranges etc. Here is some code of that:
Collider2D attackbox = Physics2D.OverlapBox (attackPos.position, new Vector2 (attackRangeX, attackRangeY), 0, whatIsPlayer);
if (attackbox != null && !isDead && characterController.isDead == false) {
targetVelocity = Vector2.zero;
animator.SetTrigger ("isPunching");
timeBtwAtk = startTimeBtwAtk;
}
and I have individual punch triggers in the animation to detect when the character is actually being hit:
public void SetColliderIndex(int spriteNum)
{
colliders[currentColliderIndex].enabled = false;
currentColliderIndex = spriteNum;
colliders[currentColliderIndex].enabled = true;
}
public void ClearColliderIndex()
{
colliders[currentColliderIndex].enabled = false;
}
void OnTriggerEnter2D(Collider2D col)
{
if (col.tag == "Player")
{
col.GetComponent<CharacterController2D> ().TakeDamage (enemyDamage);
col.GetComponent<CharacterController2D> ().canWait = false;
col.GetComponent<CharacterController2D> ().canControl = false;
}
}
When I use Physics2D.IgnoreLayerCollision (8, 9); I want both specified layers not to interact whatsoever. I don't want any weird shifts, or floating when they pass through each other. Also when I move my player agains the enemy, I don't want him to be able to push him back. I want it to be as if he is running into a wall.
I have answered something similar to this here. Also, if you don't want the player suddenly floating upon collision, you can just set the rigidbody/rigidbody2d type to kinematic, but this mean the object will not be affected by physics. you will have to do that in code.
i think your problem is that at some point the two colliders interact and/or one of the two gets activated back, but from the code that you posted i can't clearly identify the problem if there's any
I have a OnMouseOver script that will activate a guipanel (using it as a popup tooltip) on specific objects. When clicked, those objects will disappear instantly (destroyed since single use uniques) - but the guipanel stays active since there's no OnMouseExit. I can't really afford to put a BoxCollider on the background as it will mess with a lot of my other BoxCollider triggers - is there any way around this, or a better way to do what I'm doing?
Thanks!
Script:
void OnMouseOver()
{
if (gameObject.tag == "Target")
{
InfoReturned = gameObject.GetComponentInParent<InfoFill>().InfoPanel();
PopUpInfoBoxes[0].text = InfoReturned[0];
PopUpInfoBoxes[1].text = InfoReturned[1];
PopUpInfoBoxes[2].text = InfoReturned[2];
PopUpInfoBoxes[3].text = InfoReturned[3];
PopUpPanel.SetActive(true);
}
void OnMouseExit()
{
PopUpPanel.SetActive(false);
}
I need help with a game I am creating. I am building the game with the Unity engine and am pretty new to C# coding. I am creating a 2d game where the player controls the color of a square and must change it to the correct color as when it passes a certain colored object. The player changes colors great and the objects are triggers. When a player passes the trigger if its not the right color the player dies. Well that works perfect but only for the first object the next one no matter the color of the player the object dies. I have tried if's in if statements and else if's I can't seem to figure it out. Your help would be greatly appreciated!
Here is the code for the player
void OnTriggerEnter (Collider other)
{
if (other.transform.tag == "Blue") {
blue = true;
}
else {
blue=false;
}
if (other.transform.tag == "Red") {
red = true;
}
else {
red =false;
}
if (other.transform.tag == "Blue" && GameObject.Find ("Blue").GetComponent<Blue> ().mb == false) {
yield return 0;
Die ();
} else if (other.transform.tag == "Red" && GameObject.Find ("Red").GetComponent<Red> ().mr == false) {
Die ();
}
}
Here is the code for each different colored object. This one happens to be blue.
void Update () {
if (GameObject.Find ("Player").GetComponent<Movement> ().blue == true && GameObject.Find ("Player").GetComponent<Movement> ().playerBlue == true) {
mb = true;
} else {
mb = false;
}
if (!GameObject.Find("Player").GetComponent<Movement>().blue) {
mb = false;
}
}
Your execution order is a little messy, that's probably why it's not working. Try to make all your logic follow one path, not two. Right now, you are:
(1)Colliding, (2) setting some boolean variables, (3)checking some variables, (4)optionally dying.
At a separate time in a separate class, set the boolean that was set in step 1 and setting the variable that's checked in step 3
This is what we call spaghetti code. It's all twisted together. Can you think of a way to untangle it, so the two bits of logic don't mutually depend on each other?
For example, I see in the latter block of code, you're checking whether the player is blue. Could you instead run that code when the player sets blue (or sets blue=false)?
Edit: I know this isn't strictly answering your question, but this untangling is the first step of the problem solving process.