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);
}
Related
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!
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.
Anyone know how i can stop this from flicking basically I'm doing a raycast and if it hits the crafting table it it shows up a text saying press e to craft but it is flickering cause its in the void update function but if anyone knows a work around that would be nice thanks!
if(hit.collider.tag == "CraftingTable")
{
if(textIsOn)
{
noshowcraftingtext();
}
else
{
showcraftingtext();
}
}
void showcraftingtext()
{
textIsOn = true;
pressEToShowCraftingTableUI.SetActive(true);
}
void noshowcraftingtext()
{
textIsOn = false;
pressEToShowCraftingTableUI.SetActive(false);
}
The main problem of your code is that you are trying to change the state after state was changed.
That's why updated isn't the case in a big project, because all such things will lead to unpredictable or logically conflicted behavior, to avoid this you should use Data driven approach instead.
But to fix exactly your issue, you need to have a function, which isn't based on the state of the object, but returns what should happen right now.
There isn't much about what's going on inside your game, but I can guess that you are firing a raycast with mouse and then checking is it craftable or not.
public void Raycaster : MonoBehavior {
public void YourClickMethod()
{
if(hit.collider.tag == "CraftingTable")
{
hit.collider.GetComponent<UserInputReceiver>().SetClicked(this);
}
}
}
And on the object which is receiving your raycast you should add this:
public class UserInputReceiver : MonoBehavior {
private bool _isEnabled = false;
//set this inside inspector
public GameObject ObjectToEnable;
private Raycaster _currentSender = null
public void SetClicked(Raycaster sender){
_isEnabled = !_isEnabled;
_currentSender = sender;
}
public void Update(){
if(_currentSender != null && _isEnabled) {
_isEnabled = Vector3.Distance(transform.position, _currentSender.transform.position) < 1f; //set the threshold based on your unit system
}
//or another object, you can put it inside public field and enable him
//gameObject.SetActive(_isEnabled);
objectToEnable.SetActive(_isEnabled); //here you can pass a reference through inspector for your canvas
}
}
This is how you can outstand this issue with a short way.
This code will set the flag based on the received input and the distance. If you need to keep enabled text forever, than remove distance check than it will enable only with a second click, without distance dependency.
Forewords
Firstly, I know posting graphical resources for codes is not encouraged in this platform. I will also post the code but, in this particular case, I think posting a video about it is much more helpful than just posting some arbitrary code because the structuring of game projects really vary depending on their requirements. However, I still respect the platform's rules so if a mod asks me to format my question according to the community rules, I can do that or they also can simply delete my question. I respect that.
The Issue
It's actually a simple issue but it's driving me crazy because of its simplicity. I just want to fade in when I load a scene and then fade out whenever I click a button. As to how I do that, this is the video about it.
To sum up, I load another scene called "Fader" which contains a ColorRect with a black color and AnimationPlayer to change ColorRect's alpha value.
The code is below with extra comments on relevant parts:
using Godot;
using System;
public class TitleScreen : Control
{
private Button[] buttons;
private Control fader; // the scene that I inject
public override void _Ready() // when title screen gets ready
{
GD.Print("Preparing TitleScreen...");
InitButtons();
InitFader(); // initialize fader
FadeIn(); // do fade in animation
}
private void InitFader() // initializing fader
{
GD.Print("Initializing fader...");
var faderScene = (PackedScene)ResourceLoader.Load("res://components/Fader.tscn"); // load external fader scene
fader = (Control)faderScene.Instance(); // instantiate the scene
fader.SetSize(OS.WindowSize); // set the size of fader scene to the game window, just in case
var rect = (ColorRect)fader.GetNode("rect"); // get "rect" child from fader scene
rect.SetSize(OS.WindowSize); // set "rect" size to the game window as well, just in case
fader.Visible = false; // set the visibility to false
AddChild(fader); // add initialized fader scene as a child of title screen
}
private void InitButtons()
{
GD.Print("Initializing buttons...");
buttons = new Button[3]{
(Button)GetNode("menu_container/leftmenu_container/menu/start_button"),
(Button)GetNode("menu_container/leftmenu_container/menu/continue_button"),
(Button)GetNode("menu_container/leftmenu_container/menu/exit_button"),
};
GD.Print("Adding events to buttons...");
buttons[0].Connect("pressed", this, "_StartGame");
buttons[2].Connect("pressed", this, "_QuitGame");
}
private void FadeIn()
{
GD.Print("Fading in...");
fader.Visible = true; // set visibility of fader to true
var player = (AnimationPlayer)fader.GetNode("player"); // get animation player
player.Play("FadeIn"); // play FadeIn animation
fader.Visible = false; // set visibility of fader to false
}
private void FadeOut()
{
// similar to FadeIn
GD.Print("Fading out...");
fader.Visible = true;
var player = (AnimationPlayer)fader.GetNode("player");
player.Play("FadeOut");
fader.Visible = false;
}
public void _StartGame() // whenever I click start game button
{
FadeOut(); // fade out
GetTree().ChangeScene("res://stages/Demo01.tscn");
}
public void _QuitGame() // whenever I click quit game button
{
FadeOut(); // fade out
GetTree().Quit();
}
}
Seems like I can't see something. Why does it not fade in and out?
Environment
Manjaro 19.0.2
Mono JIT Compiler 6.4.0 (if it is relevant)
Godot 3.2
So, the issue was Play method on AnimationPlayer object kinda runs like async (dunno if this is the correct term for it).
Luckily, there is a feature called signals in Godot. There are animation_started and animation_finished signals on AnimationPlayer objects. Basically, I created a C# script for Fader scene, hooked the signals from player to fader as in:
animation_started to _FaderAnimationStart
animation_finished to _FaderAnimationEnd
At the end, my script looks like below:
using Godot;
using System;
public class Fader : Control
{
private ColorRect rect;
private AnimationPlayer player;
public override void _Ready()
{
GD.Print("Initializing Fader...");
rect = (ColorRect)GetNode("rect");
player = (AnimationPlayer)GetNode("player");
SetSize(OS.WindowSize);
rect.SetSize(OS.WindowSize);
Visible = false;
}
private void _FaderAnimationStart(String anim_name)
{
Visible = true;
}
private void _FaderAnimationEnd(String anim_name)
{
Visible = false;
}
}
I solved it thanks to njamster's answer and Hans Passant's comment.
However, this only solves half of the problem. Yes, the scene now fades in when it loads but it does not fade out. Given that it executes kinda-async (again, I'm not sure if this is the correct term), changing scene interrupts while running the animation. I will update the answer when I solve that problem as well.
Update
Well, I cannot seem to solve the fade out part because it requires to access parent node from initialized child scene. There are some methods I can think of.
First one is to somehow parameterize "Fader" scene. This can be done in many ways but at the end, when you initialize it from another scene, you need to cast it to Fader and I don't know if this is a valid way to do it. Another concern is standardizing this in the codebase. A similar method is discussed in here.
Second one is to write it as a plugin which has it benefits and drawbacks. C# is not really battle-tested in this particular area.
Third one is to use a state management system. Here is a redux implementation for Godot. And you need to somehow integrate it for signals, which seems like a hassle.
So, overall, I still do not know how to fade out.
Ok my problem is the oculus touchpads use the same grip controls to determine teleportation/aiming and picking up objects. I don't want to change the controls because they're intuitive, but I cant have the teleportation randomly coming up as it does now when they are picking up an object.
There is no disable function in the Oculus standard Locomotion Teleport class, and Ive tried disabling it like this:
private void Update()
{
locomotionTeleport = GameObject.FindObjectOfType<LocomotionTeleport>();
// print(GameObject.FindObjectsOfType<OVRGrabber>()[0].isGrabbing || GameObject.FindObjectsOfType<OVRGrabber>()[1].isGrabbing);
locomotionTeleport.enabled = !(GameObject.FindObjectsOfType<OVRGrabber>()[0].isGrabbing || GameObject.FindObjectsOfType<OVRGrabber>()[1].isGrabbing);
}
Where I track if object is being grabbed in the OVRGrabbers:
void OnTriggerEnter(Collider otherCollider)
{
// Get the grab trigger
OVRGrabbable grabbable = otherCollider.GetComponent<OVRGrabbable>() ?? otherCollider.GetComponentInParent<OVRGrabbable>();
if (grabbable == null) return;
if(grabbable != null)
{
isGrabbing = true;
}
But this does nothing. Nothing I try setting bool flags in the teleportation class does anything. How can I prevent teleportation if an object is being picked up, and reenable afterwards?