I have been looking on the code for ARCore Unity for a while and I want to do one simple task that is, to have a toggle button so user can place an object in the scene while knowing where to place it while the tracked planes are visible and once the user places the object, he is given the option of just visually disabling the tracked planes so it looks more realistic. I was able to do this in Android Studio by doing something like this in the main HelloArActivity.java:
if (planeToggle) {
mPlaneRenderer.drawPlanes(mSession.getAllPlanes(), frame.getPose(), projmtx);
}
this was really simple. I made a bool named planeToggle and just placed the mPlaneRenderer.drawPlanes function in an if condition. When the bool is true it displays the planes and when its false, it does not...
However, with Unity I am confused. I did something like this in the HelloARController.cs :
I made a button to togglePlanes.
Set an event listener to it to toggle a boolean variable and did something like this :
for (int i = 0; i < m_newPlanes.Count; i++)
{
// Instantiate a plane visualization prefab and set it to track the new plane. The transform is set to
// the origin with an identity rotation since the mesh for our prefab is updated in Unity World
// coordinates.
GameObject planeObject = Instantiate(m_trackedPlanePrefab, Vector3.zero, Quaternion.identity,
transform);
planeObject.GetComponent<TrackedPlaneVisualizer>().SetTrackedPlane(m_newPlanes[i]);
m_planeColors[0].a = 0;
// Apply a random color and grid rotation.
planeObject.GetComponent<Renderer>().material.SetColor("_GridColor", m_planeColors[0]);
planeObject.GetComponent<Renderer>().material.SetFloat("_UvRotation", Random.Range(0.0f, 360.0f));
if (togglePlanes == false){ // my code
planeObject.SetActive(false); // my code
} //
}
Nothing happens when I press the toggle button.
The other option I had was to make changes in the TrackedPlaneVisualizer.cs where I did something like this :
for (int i = 0; i < planePolygonCount; ++i)
{
Vector3 v = m_meshVertices[i];
// Vector from plane center to current point
Vector3 d = v - planeCenter;
float scale = 1.0f - Mathf.Min((FEATHER_LENGTH / d.magnitude), FEATHER_SCALE);
m_meshVertices.Add(scale * d + planeCenter);
if (togglePlanesbool == true) // my code
{
m_meshColors.Add(new Color(0.0f, 0.0f, 0.0f, 1.0f)); // my code
}
else
{
m_meshColors.Add(new Color(0.0f, 0.0f, 0.0f, 0.0f)); // my code
}
}
This did work. But I am experiencing delays in toggling and sometimes if two different planes have been rendered they start toggling between themselves(if one is enabled, other gets disabled). So I guess this is also not the option to go for....Anyone who can help?
Note that I am a beginner in Unity.
The sample isn't really designed to hide and show the planes, so you have to add a couple things.
First, there is no collection of the GameObjects that represent the ARCore planes. The easiest way to do this is to add a tag to the game objects:
In the Unity editor, find the TrackedPlaneVisualizer prefab and select it. Then in the property inspector, drop down the Tag dropdown and add a tag named plane.
Next, in the Toggle handler method, you need to find all the game objects with the "plane" tag. Then get both the Renderer and TrackedPlaneVisualizer components and enable or disable them based on the toggle. You need to do both components; the Renderer draws the plane, and the TrackedPlaneVisualizer re-enables the Renderer (ideally it would honor the Renderer's state).
public void OnTogglePlanes(bool flag) {
showPlanes = flag;
foreach (GameObject plane in GameObject.FindGameObjectsWithTag ("plane")) {
Renderer r = plane.GetComponent<Renderer> ();
TrackedPlaneVisualizer t = plane.GetComponent<TrackedPlaneVisualizer>();
r.enabled = flag;
t.enabled = flag;
}
}
You can also do a similar thing where the GameObject is instantiated, so new planes honor the toggle.
Related
I would like some help with figuring this out as my brain is having a funny day today at what is the best way to go about this.
I have an OnDrawGizmos as follows
Transform parentBounds;
void OnDrawGizmos()
{
parentBounds = this.gameObject.transform;
Vector3 from;
Vector3 to;
for (int a = 0; a < parentBounds.childCount; a++)
{
from = parentBounds.GetChild(a).position;
to = parentBounds.GetChild((a + 1) % parentBounds.childCount).position;
Gizmos.color = new Color(1, 0, 0);
Gizmos.DrawLine(from, to);
}
}
This code is attached on a "parent" object which contains 4 empty game objects as follows
Which produces the following Gizmo (Drawing a square based on where I put the empty objects within the parent)
I can even add more points if I desire.
SO WHAT IS THE PROBLEM ?
What I need is that if the player is not within this box area, I want the player to be destroyed. I'm not sure what the best practice is for this , should I make an array to hold the xmin, ymax, etc. ? or should I generate a new position vector on every loop ? I've tried a few ways that are not giving me the results I want and I taught to ask for some assistance.
Thank you all in advance !
If this is a 2D game (which I assume it is), how about having a script, that has reference to the player object and...
a) game is not using physics
... a public Rect boundaries. On each Update() check if player is within the boundaries and if not - destroy it.
void Update()
{
if (!boundaries.Contains(playerObject.transform.position))
{
Destroy(playerObject);
}
}
b) game is using physics
... a BoxCollider2D with the following script
void OnColliderExit2D(Collider2D collider)
{
// If collider is player object -> destroy collider.gameObject
}
private Color solveColor;
void Start()
{
Color[] colors = { Color.cyan, Color.red, Color.green, new Color(245, 195, 29), Color.yellow, Color.magenta };
int lengthOfColors = colors.Length;
int solveColor = UnityEngine.Random.Range(0, lengthOfColors);
}
private void start()
{
GetComponent<MeshRenderer>().material.color = solveColor;
}
private void FixedUpdate()
{
// Set the balls speed when it should travel
if (isTraveling) {
rb.velocity = travelDirection * speed;
}
// Paint the ground
Collider[] hitColliders = Physics.OverlapSphere(transform.position - (Vector3.up/2), .05f);
int i = 0;
while (i < hitColliders.Length)
{
GroundPiece ground = hitColliders[i].transform.GetComponent<GroundPiece>();
if (ground && !ground.isColored)
{
ground.Colored(solveColor);
}
The above code is supposed to pick one color from the colors array and assign it to both the ball and balls painting ability (whenever the ball collides with the ground it changes its color) however the paint the ball leaves is always black and the ball itself is always orange (pretty sure the ball color is coming from its default). I can't figure out why this is happening any help is very appreciated.
Thank you for your time
In the code you provided, nowhere do you set the material color of the ball again aside from Start. If you want to have the particles behind the ball leave different colors, you will need to instantiate a new instance of the material. The reason for this is because materials in Unity are default shared between all instances of that particular material.
All of this info and a bit more can be found on the Material docs page.
As you have a fixed size of colors you are using, I would instead create 6 new materials and make an array of materials instead. Now, instead of randomly picking a color, pick a material and assign it to the ball or your new instanced painting ability. I am also confused as to why you are placing your array of colors inside of your Start function. It would be localized to that function only then. You also appear to have two Start functions, which is odd. One being the Monobehaviour Start and another start. Unless that is intended, your second start will not be run unless you call it.
Now to get to the solution I was talking about.
// assign these in the inspector to your new materials
[SerializeField] private List<Material> materials = new List<Material>();
private MeshRenderer meshRender;
private void Start()
{
meshRenderer = GetComponent<MeshRenderer>();
// set our first random Material
SetNewMaterialColor();
}
private void SetNewMaterialColor()
{
meshRenderer.material = GrabNewMaterial();
}
private void FixedUpdate()
{
// Set the balls speed when it should travel
if (isTraveling) {
rb.velocity = travelDirection * speed;
}
// Paint the ground
Collider[] hitColliders = Physics.OverlapSphere(transform.position - (Vector3.up/2), .05f);
int i = 0;
while (i < hitColliders.Length)
{
GroundPiece ground = hitColliders[i].transform.GetComponent<GroundPiece>();
if (ground && !ground.isColored)
{
// change this from a color to a material instead
ground.Colored(meshRenderer.material);
// set a new material to your main object
SetNewMaterialColor();
}
}
}
private Material GrabNewMaterial()
{
return materials[UnityEngine.Random.Range(0, materials.Count)];
}
You will need to change your Colored function to take in a Material instead of a Color. If you want the implementation to be more dynamic, you can instead create an instance of your material and set the color dynamically, but as you have a fixed size of colors I do not think you need to do that.
Edit: The one other option which involves creating a new shader would be to utilize [PerRendererData] meaning each object for a property field is rendered individually. I would go with the previous option as either option using shaders or instanced materials is a bit more complex.
You would need to use a MaterialPropertyBlock and can then assign the color when you want. It would look something like
public void SetNewColor()
{
// create a new material property block
MaterialPropertyBlock tmpBlock = new MaterialPropertyBlock();
// grab the current block from our renderer
meshRender.GetPropertyBlock(tmpBlock);
// set our changes to the block
tmpBlock.SetColor("_Color", YourColorHere);
// now apply our changes
tmpRend.SetPropertyBlock(tmpBlock);
}
And you would need to create a new shader that laters the Main Color property by using the PerRendererData attribute.
Properties
{
[PerRendererData]_Color("Main Color", Color) = (1,1,1,1)
...
Also, one other question I have is why you are using Physics.OverlapSphere instead of just an OnCollisionEnter? Or if your game is 2D, then OnCollisionEnter2D and let the physics engine handle how collisions work, then just change the colors when the collision occurs?
Edit: Here are the answer to your questions - let me know if you have more.
In the line "[SerializeField] private List materials = new
List();" which section do I need to replace with the
materials and how?
The line as is is fine. By using [SerializeField] it exposes this list to the editor. You will want to create several new duplicate materials that use your 6 different colors. Instead of setting the colors, you will be setting materials now. What I mean by inspector and editor is you can find the object that has this script on it in Unity, select it (it must be a Prefab or in the scene), then a tab of the Unity editor will populate with information about this object. Find the script portion and find the field materials. There should be a drop-down arrow, click it and set the number to 6 (or however many material swaps you want). Now create 6 new materials with your colors and drag them into the boxes that appeared.
Would it be something like writing "./Materials/Ball 1" in the () for
example?
Nope! You would be assigning this data in the inspector, so the data would be stored in the list without referencing them in code.
And I'm not sure how to assign this to my ball using "[SerializeField]
private GameObject paintObject = null;"
Similarly, this would appear in the inspector. However, remove this line as I misunderstood your original question and accidentally left this in. I assumed that your paint object was a Prefab that you were spawning after the ball bounced, not the ground that you were changing the color of.
I get the error "Argument 1: cannot convert from
'UnityEngine.Material' to 'UnityEngine.Color'"
Yep! So as I mentioned in the comments, your function call to your paint object is most likely currently taking a Color parameter. As I changed your implementation to instead directly set Material, you will need to change how that function signature. Specifically the line:
ground.Colored(meshRenderer.material);
You have some object ground that is of type GroundPiece and has a function called Colored. I assume it currently look something like:
public void Colored(Color color){...}
You want to change this instead to:
public void Colored(Material mat{...}
After changing it, instead of changing the ground's color in this script, you would change its material directly. Let me know if you have more questions.
I have one problem. I want my prefabs to spawn every time my player picks them up. I did research on Google and YouTube and I tried to use the random function and instantiate. I don't know how to use them. I wrote this code I saw on YouTube and my prefab Sphere moves like 1cm to z position. I want to every time when I pick up object or my player go to spawn more of this on the z position. How do I do this?
My smaller script:
public GameObject Sphere;
public float zrange;
// Use this for initialization
void Start () {
RandomPosition();
}
void RandomPosition()
{
zrange = Random.Range(0f, 2f);
this.transform.position = new Vector3(0, 0, zrange);
}
You achieve that by not messing with the x and y values (your code sets them both to 0).
Vector3 p = transform.position;
p.z = zrange;
transform.position = p;
This assumes that your code to instantiate the object is already correctly placing the object. If not, more information is needed.
I have created a game in which you can control X characters at the same time in the same form and they can die at any time. My problem is when I want the game camera to include all these gameobjects.
I thought that a good option is to calculate the central point between the gameobjects in the scene and make the camera follow that point at a certain distance.
I already have the camera code, but I still need to know how to get that central point or another way of doing it. In addition, the camera does not follow any of the axes (X, Y, Z) linearly, since it is placed in such a way that is the view is isometric (the game is in 3D).
As a last important fact, it is that all gameobjects that are running in the game (that are alive), are stored in a public static List <GameObject> to be able to access the components of these gameobjects at any time. Also, if a character (gameobject) dies or is born, the list is updated without problems.
I leave you a graphic example with three different cases, being the black points, the characters that are in the scene (gameobjects) and the red points, the central point (vector) that I would like to find.
Also, I leave the camera code so you can test if you have any solution:
public class Camera_Movement : MonoBehaviour {
Vector3 newPos;
public static List<GameObject> playersInGame = new List<GameObject>();
void Update() {
// Get Central Vector
// Replace playersInGame[0].transform.position with central vector
//newPos = Vector3.Lerp(gameObject.transform.position, "central vector", Time.deltaTime);
newPos = Vector3.Lerp(gameObject.transform.position, playersInGame[0].transform.position, Time.deltaTime);
gameObject.transform.position = new Vector3(newPos.x, newPos.y, newPos.z);
}
}
Thank you very much in advance!
You need to take the average x and and average y.
That would look like the following:
var totalX = 0f;
var totalY = 0f;
foreach(var player in playersInGame)
{
totalX += player.transform.position.x;
totalY += player.transform.position.y;
}
var centerX = totalX / playersInGame.Count;
var centerY = totalY / playersInGame.Count;
Let me know if this works for you (don't have access to Unity at the moment), but I put together an example here: https://dotnetfiddle.net/jGd99I
To have a solution that your camera can be best positioned to see all of your objects, then try this:
public Vector3 FindCenterOfTransforms(List<Transform> transforms)
{
var bound = new Bounds(transforms[0].position, Vector3.zero);
for(int i = 1; i < transforms.Count; i++)
{
bound.Encapsulate(transforms[i].position);
}
return bound.center;
}
Working on 2D mode, I have a script attached to a Sprite. The Update() part is as follow:
void Update () {
if (Input.GetKeyDown ("left")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position - speedX);
} else if (Input.GetKeyDown ("right")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position + speedX);
} else if (Input.GetKeyDown ("up")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position + speedY);
} else if (Input.GetKeyDown ("down")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position - speedY);
}
}
where cursor is a Prefab linked in Inspector. The cursor prefab has only 2 components, Sprite Renderer (to define the image), and the Rigidbody 2D, which setting is as follow:
Mass = 1
Linear Drag = 0
Angular Drag = 0
Gravity Scale = 0
Fixed Angle = true
Is Kinematic = false
Interpolate = None
Sleeping Mode = Start Awake
Collision Detection = Discrete
But when I press the arrow keys, the sprite showing on screen does not move. What did I miss?
Tried to add Debug.Log() inside the if case, it really enters the case. And no error occurred.
Note: speedX = new Vector2(1,0); and speedY = new Vector2(0,1);
You're trying to move an object that doesn't exist from the game's perspective. Assigning cursor a prefab by dragging it from the assets library, is like assigning a blueprint. Telling the prefab to move is like yelling at a car's blueprint to accelerate :)
There are two general solutions.
1) Save a reference
Instantiate the prefab and save the resulting reference to manipulate your new object. You can directly cast it to Rigidbody2D if you're mainly interested in using the rigidbody. The cast will only work if your prefab variable is of the same type, otherwise it will raise an exception. This way will ensure that your prefab always contains a Rigidbody2D or you can't even assign it in the editor.
public Rigidbody2D cursorPrefab; // your prefab assigned by the Unity Editor
Rigidbody2D cursorClone = (Rigidbody2D) Instantiate(cursorPrefab);
cursorClone.MovePosition (cursorClone.position - speedX);
2) Assign the script to the prefab
Depending on your game and what you want to achieve, you can also add a script directly to the prefab. This way every instance of it would just control itself.
void Update () {
if (Input.GetKeyDown ("left")) {
rigidbody2D.MovePosition (rigidbody2D.position - speedX);
} else { //...}
}