I have created a floor where by gazing on it the material changes for every 2 seconds. Now I wanted to create a material picker on the scene. Where the user can choose from the given 3 options of the materials and by clicking on it the selected material should apply to the floor. How to do it?
The source code for gazing floor to change material:-
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Make sure to change the class name (CCSphere) to whatever you called your
//script.
public class tochangematerial : MonoBehaviour
{
public float gazeTime = 2f;
private float timer;
private bool gazedAt;
public Material[] materials;//Allows input of material colors in a set size of array;
public Renderer Rend; //What are we rendering? Input object(Sphere,Cylinder,...) to render.
private int index = 1;//Initialize at 1, otherwise you have to press the ball twice to change colors at first.
// Use this for initialization
void Start()
{
Rend = GetComponent<Renderer>();//Gives functionality for the renderer
Rend.enabled = true;//Makes the rendered 3d object visable if enabled;
}
void Update()
{
if (gazedAt)
{
timer += Time.deltaTime;
if (timer >= gazeTime)
{
if (materials.Length == 0)//If there are no materials nothing happens.
return;
index += 1;//When mouse is pressed down we increment up to the next index location
if (index == materials.Length + 1)//When it reaches the end of the materials it starts over.
index = 1;
print(index);//used for debugging
Rend.sharedMaterial = materials[index - 1]; //This sets the material color values inside the index
timer = 0f;
}
}
}
public void pointerenter()
{
//Debug.Log("pointer enter");
gazedAt = true;
}
public void pointerexit()
{
//Debug.Log("pointer exit");
gazedAt = false;
}
}
Edited code:-
using UnityEngine;
using System.Collections;
// Change renderer's material each changeInterval
// seconds from the material array defined in the inspector.
public class color2 : MonoBehaviour
{
public Material[] materials;
public float changeInterval = 0.33F;
public Renderer rend;
void Start()
{
rend = GetComponent<Renderer>();
rend.enabled = true;
}
public void Update()
{
if (materials.Length == 0)
return;
// we want this material index now
int index = Mathf.FloorToInt(Time.time / changeInterval);
// take a modulo with materials count so that animation repeats
index = index % materials.Length;
// assign it to the renderer
rend.sharedMaterial = materials[index];
}
}
I used this code to change the color of a cube if i pressed a button. But it's not working. I have added this code to cube and in button I have attached this script and function. If I pressed the button the color of cube does not change.
I have added this script to cube and then in button I have dragged and dropped the gameobject(cube) and triggered the function void update()
Edited again:-
Now I can change the materials of the 3d model for eg:if it is a chair I am able to change the materials of chair by clicking on a button. How can I change the different models? eg:in chair there are different models if a user clicked on a button it should produce different models how to do it?
The simpliest way would be to create a function that takes an int index and in that function change Rend's material to materials[index], then create an UI canvas with three buttons, each of the buttons would trigger that function but pass different int.
Related
The objects are being set to out of the canvas
The objective of setting them is to show the user inventory with only the items that the user have
It's setting them active normally, and in the order that the user got it, but in wrong positioning
No error messages were sent
Before setting the positions array, i checked what were the positions in the scene to put it right on the array
But the objects are setting in the wrong place
I put this debug logs to try to see the problem
But it says the positions that i set
This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class Filters
: MonoBehaviour
{
[Header("Items")]
public GameObject doubleSpeed;
public GameObject doubleJump;
public GameObject superForce;
public GameObject noGravity;
public GameObject noHit;
public GameObject apple;
private GameObject[] items;
public int index = 0;
public float[] position = new float[6] { -125.8f, -78.6f, -24.1f, 23.1f, 80.69913f, 36.3375f };
private float x;
// Update is called once per frame
void Update()
{
Player player = FindObjectOfType<Player>();
Filter(doubleJump, player.doubleJumps);
Filter(doubleSpeed, player.doubleSpeeds);
Filter(superForce, player.superForces);
Filter(noGravity, player.noGravitys);
Filter(noHit, player.noHits);
Filter(apple, player.apples);
items = GameObject.FindGameObjectsWithTag("Have");
if(items.Length != 0)
{
for(int i = 0; i < items.Length; i++)
{
items[i].transform.position = new Vector3(position[i], 52.8f , 1f);
items[i].SetActive(true);
Debug.Log("Changed the position and set active new position:" + items[i].transform.position);
}
}
}
void Filter(GameObject item, int quantity)
{
if(quantity < 1)
{
item.SetActive(false);
Debug.Log("less than 1");
}
else
{
item.SetActive(true);
Debug.Log("set active");
item.tag = "Have";
Debug.Log("set the tag");
}
}
}
In general there is a difference between the localPosition - the position relative to the parent object and usually the one displayed in the Unity Inspector - and the absolute world space position you are setting in
items[i].transform.position = new Vector3(position[i], 52.8f , 1f);
Further there is an even bigger difference when dealing with a RectTransform. What you see and edit in the Inspector is the RectTransform.anchoredPosition which is relative to the parents RectTransform and additionally takes the Anchors and Pivot settings into account.
So try this instead
items[i].GetComponent<RectTransform>().anchoredPosition3D = new Vector3(position[i], 52.8f , 1f);
anchoredPosition3D is basically the same as anchoredPosition but additionally allows to set the delta in the Z axis
In general for your usecase I would recommend to use a HorizontalLayoutGroup and rather let the UI handle the placement itself. You would then simply use SetActive to hide and show items.
this should be a very simple answer. I'm following a Unity with C# tutorial for making a simple Space Invaders game, and at one point it is shown that when our enemyHolder object has no child objects left (when all enemies are destroyed) the attached text under the winText function should be displayed.
So we have
if (enemyHolder.childCount == 0)
{
winText.enabled = true;
}
When I run the code the text isn't displayed after the enemies are destroyed and no child object is left. It's like the code stops getting read at that point, although the character is still movable and you can generate new shots.
If I create two "Enemy" child objects and tell it to display the winText rather when the childCount reaches 1, it does work.
So why is it not working when the function calls for == 0?
EDIT: Complete class code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EnemyController : MonoBehaviour
{
private Transform enemyHolder;
public float speed;
public GameObject shot;
public Text winText;
public float fireRate = 0.997f;
// Start is called before the first frame update
void Start()
{
winText.enabled = false;
InvokeRepeating("MoveEnemy", 0.1f, 0.3f);
enemyHolder = GetComponent<Transform>();
}
void MoveEnemy()
{
enemyHolder.position += Vector3.right * speed;
foreach (Transform enemy in enemyHolder)
{
if (enemy.position.x < -10.5 || enemy.position.x > 10.5)
{
speed = -speed;
enemyHolder.position += Vector3.down * 0.5f;
return;
}
if (enemy.position.y <= -4)
{
GameOver.isPlayerDead = true;
Time.timeScale = 0;
}
if (enemyHolder.childCount == 1)
{
CancelInvoke();
InvokeRepeating("MoveEnemy", 0.1f, 0.25f);
}
if (enemyHolder.childCount == 0)
{
winText.enabled = true;
}
}
}
}
Your code is inside the void MoveEnemy() function.
I'm assuming your script is attached to in-game enemies. Your code doesn't run because the MoveEnemy function no longer runs if there are no enemies.
So, you need to handle enemy movement and scene handling in different scripts.
The code that checks the enemy holder's number of children should be placed inside a void Update() function. This Update() function should be placed on an object that never gets deleted. Its advantage is that it runs every frame.
As a convention, devs generally use separate empty objects or even the camera to attach scripts which contain Update functions that handle the scene. Good luck!
Read more on Update
so the player hits a button to create a box. I need this box to be randomly varying between a few colors. I also need this box to have a tag corresponding to said color. Green box - "greenBlock" tag etc.
I have instantiated the box and then try to change it's material with material.color. It doesn't do anything. I've seen suggestions of sharedMaterial but having tried that found it just ends up changing the color of every game object in the scene. I think I'm fetching the box prefabs renderer correctly? Any help would be appreciated!
Here's what I have so far:
public class ButtonScript : MonoBehaviour
{
public GameObject Box;
public Transform spawnpoint1;
public Transform spawnpoint2;
public Rigidbody2D player;
public Renderer boxRenderer;
[SerializeField]
private Color boxColor;
[SerializeField]
private AudioSource actionSound;
// Update is called once per frame
private void Start()
{
//boxRenderer = Box.gameObject.GetComponent<Renderer>();
boxRenderer = GameObject.Find("Box").GetComponent<Renderer>(); // Find the renderer of the box prefab
}
public void OnTriggerStay2D(Collider2D col)
{
if (player) // If it's the player in the collider trigger
{
if (Input.GetKeyDown(KeyCode.E))
{
Instantiate(Box, spawnpoint1.position, Quaternion.identity);
boxRenderer.material.color = boxColor; // change the color after it is instantiated
actionSound.Play();
}
}
}
}
boxRenderer.material.SetColor("_Color", boxColor);
or
boxRenderer.material.color = new Color(0.5, 0.5, 0.0, 1.0);
And when you instantiate the box, you need to get the render for the box at this point as it is a new object. So:
if (Input.GetKeyDown(KeyCode.E))
{
Box = Instantiate(Box, spawnpoint1.position, Quaternion.identity);
boxRenderer = Box.transform.GetComponent<Renderer>();
boxRenderer.material.color = boxColor; // change the color after it is instantiated
actionSound.Play();
}
Note you have creating a New color and assigning it, you can't modify the color there as it may be used on other objects that are using the same material.
Check out SetColor in the docs, that is setting the shader property called _Color which is the default color element in shaders, you can of course have more depending on the shader.
Hi everyone I have a little problem with my bow shooting script. I mean i want to shoot an arrow when play the one of last frames from my animation. I try to do it by setting an firePoint GameObject, put it by recording in my Animation Tab in desired frame. It's of course disabled but its enabled when animation plays and then its again disabled. So the problem is:
- When i hit button which match my Shooting input, the animation plays,
- My Instantiate appears and it produces multiple arrows,
- When its disabled it stops to produce arrows.
I want to produce only one arrow. Could anyone help?
CombatScript.cs:
/* private bool shootBow;
* public bool needReload = false;
* public float reloadTime = 1.5f;
* public float realoadCD;
*/
public void RangeAttack()
{
if (needReload == false && gameObject.GetComponent<PlayerControls>().grounded == true && Input.GetButtonDown("Ranged"))
{
animator.SetTrigger("shootBow");
attack1 = false; // Melle attacks sets to false in case of lag or smth.
attack2 = false;
attack3 = false;
needReload = true;
if (needReload == true)
{
reloadCD = reloadTime;
}
}
if (reloadCD > 0 && needReload == true)
{
reloadCD -= Time.deltaTime;
}
if (reloadCD <= 0)
{
reloadCD = 0;
needReload = false;
}
if (firePoint.gameObject.activeSelf == true)
{
Instantiate(Missile, new Vector3(firePoint.position.x + 1, firePoint.position.y), firePoint.rotation);
Debug.Log("It's a bird, a plane, no.. it's arrow.");
}
}
Arrow Controller.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowController : MonoBehaviour {
public float speed;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
GetComponent<Rigidbody2D>().velocity = new Vector2(speed, GetComponent<Rigidbody2D>().velocity.y);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Enemy")
{
Destroy(collision.gameObject);
}
Debug.Log("Arrow Broke");
Debug.Log(gameObject.name);
//Destroy(gameObject);
}
public void OnCollisionEnter2D(Collision2D collision)
{
}
}
Example of my situation:
Example of true/false needReload statement:
in right Inspector you have my Player informations, in left (or bottom) Inspector you have Missile (Arrow) inspector
You can use Animation Events and for example a boolean that is your firerate.
And you Code can look something like this:
float LastShoot;
float FireRate = 10;
public void OnShoot()
{
if (LastShoot <= 0)
{
//Shoot your arrow
LastShoot = FireRate;
}
}
void Update()
{
LastShoot -= 0.5f;
}
But i don't know if this is the best solution to calculate a firerate. If someone knows a better one feel free and edit my awnser :)
Okey... couple of hours later (which I wasted...). The answer was ridiculously easy. For future "problems" like that... the thing was to create in my script function called "ProduceArrow()"and (additionaly to what i did) something like Animation Event it's in Animation Tab, when you create your animation timeline you just need to call it clicking right mouse button and then choose it and pick proper function.
Some feedback - gif
The way your code works right now, Instatiate will be called every frame. So one button click, which is probably longer than one frame, will trigger multiple arrows, so you need to set a condition for when you want Instantiate to be called. You already calculate a reload time, which is exactly what you need. You just need to include it in your if-statement like follows:
if (firePoint.gameObject.activeSelf == true && !needReload)
{
Instantiate(Missile, new Vector3(firePoint.position.x + 1, firePoint.position.y), firePoint.rotation);
Debug.Log("It's a bird, a plane, no.. it's arrow.");
}
Within my 2d game i wsih to have a number of OnGui elements there for the user to select, however, the cursor that im using is another ongui element(using kinect to navigate) is this possible by any chance, at the moment im using planes but i will be zooming in and out of the camera so ill essentially need them attatched to the screen. Any ideas, suggestions or workarounds.
THis is currently my cursor.
using UnityEngine;
using System;
using System.Collections;
public class PillarAgent : MonoBehaviour {
public SkeletonWrapper sw;
public Vector3 distance;
public float progress =0f;
public Texture2D cursor;
public Texture2D load;
public Camera mainCam;
public float startTime;
private int roundedRestSecounds;
// Use this for initialization
float differencex = 0;
float differencey = 0;
void Start () {
distance =new Vector3(0f,0f,0f);
}
float translate(float value, float leftMin, float leftMax,
float rightMin,float rightMax)
{
float leftSpan = leftMax - leftMin;
float rightSpan= rightMax - rightMin;
float valueScaled = (value-leftMin)/(leftSpan);
return rightMin+(valueScaled * rightSpan);
}
// Update is called once per frame
void Update () {
if (sw.pollSkeleton())
{
distance.x=sw.bonePos[0,0].x - sw.bonePos[0,7].x;//5 is left shoulder
distance.y=sw.bonePos[0,0].y -sw.bonePos[0,7].y;
differencex=translate(distance.x,.6f,0,0,Screen.width);
differencey=translate(distance.y,-.5f,0,0,Screen.height);
//Debug.Log();
float width = sw.bonePos[0,5].x+ sw.bonePos[0,9].x;
float height =sw.bonePos[0,4].y- sw.bonePos[0,0].y;
float heightdiv= (height/2)+sw.bonePos[0,0].y;
}
}
void OnGUI() {
//left top width height
Rect r = new Rect(differencex,differencey,80,50);
GUI.Label(r,cursor);
GUI.BeginGroup(new Rect(differencex,differencey+50,50*Mathf.Clamp01(progress),15));
//Debug.Log(progress);
GUI.DrawTexture(new Rect(0,0,50,50),load);
GUI.EndGroup();
transform.position =mainCam.ScreenToWorldPoint(new Vector3(differencex,Screen.height-differencey,50));
//mainCam.fieldOfView()
}
void OnCollisionStay(Collision Other)
{
startTime+=Time.deltaTime;
if(Other.gameObject.GetComponent(typeof(TextControl)))
{
roundedRestSecounds=Mathf.CeilToInt(Time.time);
progress = Time.time *0.2f;
CurrentState=true;
}
else if(Other.gameObject.tag==("Scalpal")){
progress = startTime *0.5f;
//scallpall activated
//
}
}
void OnCollisionExit(Collision Other){
startTime =0f;
progress =0f;
}
public Boolean CurrentState{get;set;}
}
The next class is essentially the class in which i pick up my tools, currently this code doesnt work(not sure why), but what i wish to do is select some tools which show up on the screen so that i can use them,for e.g pick up paint brush start painting bricks or what not. at the moment i have my tools on a plane, i wish to always have them on the screen at all times when the camera moves.
using UnityEngine;
using System.Collections;
public class SelectTool : MonoBehaviour {
public Tools tools;
public float startTime;
public bool ScalpalSelected;
public GameObject selectedTool;
void Start()
{
tools = this.GetComponent<Tools>(); //in order to use this tools muyst be attached to the game object
//this is essentially saying with regards to this game object get the component named tools
}
void update()
{
}
void OnCollisionStay(Collision Other)
{
startTime +=Time.deltaTime;
if(startTime >5f){
if(Other.collider.tag==("Scalpal"))
{
selectedTool = Other.collider.gameObject;
Debug.Log(selectedTool+" What in gods good name is:" +tools.utilities[0]);
}
else {
selectedTool=null;
}
if(selectedTool){
for(int i=0;i<tools.utilities.Length;i++)
{
}
}
ScalpalSelected=true;
renderer.material.color = Color.yellow;
}
}
void OncollisionStay(Collision other){
startTime = 0f;
}
}
From the comment section to the question I will assume that you want to know how to do this:
"... you want your plane objects to move together with the camera ..." - Steven Mills
"thank you #StevenMills Do you have any example of how this can be done?" j bel
While the answer provided in the comments is to just manually add the planes as children of the camera (a very straightforward, manual approach), I will give another way to do this through scripting (in light of maybe this will help someone else out disregarding the likeliness of someone using this solution).
The idea of this is to create a script (thus following being attached to the MainCamera) that will search through all of the GameObject in the Object Hierarchy with the method GameObject.FindGameObjectsWithTag. Once we have all our GameObject with the associated Tag, we can then loop through the array and parent the attached script's GameObject to each.
public class ParentGameObjects : MonoBehaviour {
//The tag to search for on all game objects in the hierarchy
public String objectTag;
//The game objects that we will parent the main camera to
private GameObject[] children;
//It's not necessary to store references to the children but if you want to modify them at some point you will be able to
void Start() {
//Find all game objects with the tag we want
children = GameObject.FindGameObjectsWithTag(objectTag);
//Loop through all of the game objects found and parent this object's transform
for(int i = 0; i < children.Length; i++) {
children[i].transform.parent = transform;
}
}
}
Now, there are a few things you have to do for this script to work:
Attach this script to any GameObject that you want to parent other objects to.
In the Inspector of the GameObject that the script is attached to, enter in the name of the Tag you want to use.
For all GameObject(s) in the Hierarchy that should be added as children, assign the same Tag.
Of course there are other things you can do like, for example, instead of only searching for one Tag be able to search for multiple but that requires a bit (not exactly much) more work. Nonetheless, I hope this will at least be useful information to someone on how parenting works via scripting.