How do I make the Scoreboard on the side not be visible when the Debug HUD is open? - minecraft-fabric

I'm working on a 1.16.5 Minecraft client-side mod and I am trying to get the scoreboard to be invisible when the Debug HUD is visible. I have a basic mixin to the scoreboard's rendering function but I need to be able to check for whether the DebugHud is visible or not.
Code:
#Mixin(InGameHud.class)
public class MScoreboardHUD {
#Inject(at = #At("HEAD"), method = "renderScoreboardSidebar")
private void init(CallbackInfo info) {
// soon
}
}

Answer
Using MinecraftClient.options.debugEnabled will tell you whether the F3 menu is active.
Final Code
This uses client as the MinecraftClient object, and info.cancel() to cancel the scoreboard render
#Mixin(InGameHud.class)
public class MScoreboardHUD {
#Inject(at = #At("HEAD"), method = "renderScoreboardSidebar")
private void init(CallbackInfo info) {
if (client.options.debugEnabled) {
info.cancel();
}
}
}

Related

Unity editor scripting : how to execute two different actions with the same button?

I wrote some code to add a button to my custom editor in Unity. What I'd like is the following. When I click the button once, a component is added, and when I click again the component is removed. In the simple code below, I just try to print "Add" or "Remove" when clicking the button. I noticed that the variable toggleRigidBody takes the value true but then take the value false right after. It doesn't stay "true" even though I never explicitly change it in the code. The "else if" is never fired. I'm not sure why.
using UnityEditor;
using UnityEngine;
using SS3D.Engine.Inventory;
[CustomEditor(typeof(ContainerController))]
public class ContainerControllerEditor : Editor
{
private bool toggleRigidBody = false;
public override void OnInspectorGUI()
{
DrawDefaultInspector();
ContainerController containerController = (ContainerController)target;
Debug.Log(toggleRigidBody);
bool buttonPressed = GUILayout.Button("AddAttachedContainer");
if (buttonPressed && toggleRigidBody == false)
{
Debug.Log("Add");
toggleRigidBody = true;
}
else if (buttonPressed && toggleRigidBody == true)
{
Debug.Log("Remove");
toggleRigidBody = false;
}
}
}
My code only print "Add" when I click the button. What's happening here ?
The main problem here is that the editor instance is created when the object is clicked and the Inspector loaded. And then it is destroyed as soon as the object loses focus and the Inspector not shown for this object anymore.
=> Your flag toggleRigidBody is not persistent!
What you rather want to do is serialize the flag inside your object or even better: Serialize the reference itself.
This way you
Have already access to the reference in your script in case you need it on runtime
Have the reference in the editor for a) checking if it exists and b) being able to remove it directly
So having your class like
public class ContainerController : MonoBehaviour
{
// Store the reference in a field
[SerializeField] private Rigidbody _rigidbody;
...
}
The editor could look like
[CustomEditor(typeof(ContainerController))]
public class ContainerControllerEditor : Editor
{
private SerializedProperty _rigidbody;
ContainerController containerController;
private void OnEnable()
{
_rigidbody = serializedObject.FindProperty("_rigidbody");
containerController = (ContainerController)target;
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
// Loads all current serialized values from the target into the serialized properties
serializedObject.Update();
// If the _rigidbody field is not assigned
// try GetComponent as fallback
if(!_rigidbody.objectReferenceValue) _rigidbody.objectReferenceValue = containerController.GetComponent<Rigidbody>();
// simply put everything that belongs to one button click inside one if block
// this is easier to maintain and read
// Of course alternatively you could also simply have two completely different buttons to display
// depending on the value of "_rigidbody.objectReferenceValue"
if(GUILayout.Button(_rigidbody.objectReferenceValue ? "Remove Rigidbody" : "Add Rigidbody")
{
// Is there a Rigidbody?
if(_rigidbody.objectReferenceValue)
{
// Yes -> destroy it
// There are two different destroy methods depending whether you are
// in Play mode or Edit mode
if(Application.isPlaying)
{
Destroy(_rigidbody.objectReferenceValue);
}
else
{
DestroyImmediate(_rigidbody.objectReferenceValue);
}
}
// Otherwise the field is currently not set and no component was found using GetComponent
else
{
// Add the component via the ObjectFactory
// this enabled undo/redo and marks the scene dirty etc
// and assign it to the serialized property
_rigidbody.objectReferenceValue = ObjectFactory.AddComponent<Rigidbody>(target.gameObject);
}
}
// Writes back all modified properties to the target and takes care of Undo/Redo and marking dirty
serializedObject.ApplyModifiedProperties ();
}
}
The editor object is created when it's being displayed, and destroyed when it's not, so for your data to persist you will need to store the values somewhere else. So that's definitely what is happening. The easiest way to have a value of a variable to be persistent between sessions you would use EditorPrefs to save the variable values. Thats the easiest way you can go about it, so you would use this to save the toggleRigidBody value.
https://docs.unity3d.com/ScriptReference/EditorPrefs.SetBool.html

SteamVR: Correct way to get the input device triggered by an action and then get it's corresponding Hand class?

I have an action that is mapped to both my left amd right hand triggers on my VR controllers. I would like to access these instances...
Player.instance.rightHand
Player.instance.leftHand
...depending on which trigger is used but I can't fathom the proper way to do it from the SteamVR API. So far the closest I have gotten is this...
public SteamVR_Action_Boolean CubeNavigation_Position;
private void Update()
{
if (CubeNavigation_Position[SteamVR_Input_Sources.Any].state) {
// this returns an enum which can be converted to string for LeftHand or RightHand
SteamVR_Input_Sources inputSource = CubeNavigation_Position[SteamVR_Input_Sources.Any].activeDevice;
}
}
...am I supposed to do multiple if statements for SteamVR_Input_Sources.LeftHand and SteamVR_Input_Sources.RightHand? That doesn't seem correct.
I just want to get the input device that triggered the action and then access it using Player.instance.
I was also looking for an answer to this. I've for now done what I think is what you mean with the if-statements. It works, but definitely not ideal. You want to directly refer to the hand which triggered the action, right?
With the 'inputHand' variable here I get the transform.position of the hand from which I will raycast and show a visible line. I could have put a separate instance of a raycastScript like this on each hand, of course, but I wanted to make a 'global' script, if that makes sense.
private SteamVR_Input_Sources inputSource = SteamVR_Input_Sources.Any; //which controller
public SteamVR_Action_Boolean raycastTrigger; // action-button
private Hand inputHand;
private void Update()
{
if (raycastTrigger.stateDown && !isRaycasting) // If holding down trigger
{
isRaycasting = true;
inputHand = inputChecker();
}
if (raycastTrigger.stateUp && isRaycasting)
{
isRaycasting = false;
}
}
private Hand inputChecker()
{
if (raycastTrigger.activeDevice == SteamVR_Input_Sources.RightHand)
{
inputHand = Player.instance.rightHand;
}
else if (raycastTrigger.activeDevice == SteamVR_Input_Sources.LeftHand)
{
inputHand = Player.instance.leftHand;
}
return inputHand;
}

Loader during Unity IAP Callback

I want to put loader in between dialog boxes come up for the purchase. What is the way for this?
Because when game player press Buy button, he should require to wait for 5 to 10 second depends on internet speed and server response and this process happed 2 to 3 times because multiple dialogs come up within screen.
So in this case, may be player can leave the screen. I want to put the loader so that game player realise that some processing is running in background, he required to wait for some time.
At present I was following completely this code for Unity IAP setup.
Integrating Unity IAP In Your Game
I assume this is for mobile platform but even if its not still the following can be considered:
Simple solution is to create a full screen Image (UI/Panel) object in your UI to block clicks. I would use Animator component (with triggers) to display this panel in front of other UI when there is a background process running.
public class Loader : MonoBehaviour
{
public static Loader Instance;
Animator m_Animator;
public bool Loading {get; private set;}
void Awake()
{
Instance = this; // However make sure there is only one object containing this script in the scene all time.
}
void Start()
{
//This gets the Animator, which should be attached to the GameObject you are intending to animate.
m_Animator = gameObject.GetComponent<Animator>();
Loading = false;
}
public void Show()
{
Loading = true;
m_Animator.SetBool("Loading", Loading); // this will show the panel.
}
public void Hide()
{
Loading = false;
m_Animator.SetBool("Loading", Loading); // this will hide the panel.
}
}
Then in any script which manipulates UI:
public void BuyButtonClicked()
{
Loader.Instance.Show();
// process time taking stuff
Loader.Instance.Hide();
}
You can also create any kind of loading animation as child of panel object using simple images and animation tool inside Unity (for example rotating animation (use fidget spinner, its cool)).
And in case of Android where user have option to leave screen by pressing OS back button you can prevent going back by checking if any loading is in progress by following example:
// code for back button
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
BackButtonPressed();
}
}
void BackButtonPressed()
{
if(Loader.Instance.Loading)
return;
// use back button event. (For example to leave screen)
}
Hope this helps ;)

Ending an application(game) and exit or and start a new one

So how does one end an application/game on a button click and exit as if the window red close symbol (X) has been clicked on or better still
how does one end the current application and without closing the whole window / stage starts a new one ?
so for example we have something like
public class Main extends Application
{
public Scene scene ;
private parent createContent()
{
// root pane, nodes and everything is here
//which makes up the game
//return root;
}
#Override
public void start(Stage stage)
{
scene = new Scene(createContent());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {launch(args); }
}
So at the end of the current game, the user should be giving the option to start a new game or to exit the application completely by clicking on buttons. If he should click on exit game then the game should close as if he has pressed on the window red x close symbol.
If the user should click on start a new game, then the prefered behavior will be for method
private parent createContent()
to start all over again, but of course all stages and nodes created in the previous calls of createContent() should be eliminated..
How can this be done?
I have the similar workflow in my project and I implemented the next way.
Register a handler for OnCloseRequest:
stage.setOnCloseRequest(windowEvent -> appToBeClosed(stage, windowEvent));
Below methods to show a dialog with a question. Only if an user decided to stay you should consume the current window event and do something otherwise the application will be closed:
private void appToBeClosed(Stage notUsedStage, WindowEvent windowEvent) {
if (hasNotSavedEvents()) {
final Optional<ButtonType> userResponse = alertAboutNotSavedChangedEvents(
"alert.changed.header", "alert.changed.content");
if (userResponse.isPresent() && userResponse.get() == ButtonType.NO) {
windowEvent.consume();
}
}
}
private Optional<ButtonType> alertAboutNotSavedChangedEvents(String headerResourceKey,
String contentResourceKey) {
final Alert alert = new Alert();
// TODO prepare alert as you wish...
return alert.showAndWait();
}
I hope the main idea is clear and you will be able to adopt it to your project.
Let me know your questions.

JavaFx set volume on MediaPlayer not working

This question has been asked before but to date there is no answer. I have just struck the same problem. I traced into the mediaplayer source and all is well until I get to gstSetVolume in GSTMediaPlayer for which I don't have the source.
At that point the volume value is still correct.
After setting the volume System.out.println(player.getVolume()); reports the correct value (0.0 - 1.0) BUT the volume has not changed. If the slider is set to 0, the audio is muted. Any other value is full volume.
Platform is Netbeans 8.1 on Windows 7 with JavaFX-8 and JDK-8 and the media source is a standard mp4 which works on all tested players (Windows, VLC etc).
Here is the code attached to a MediaView (which works well):
Runnable videoLoop;
PlayListViewController playListViewController;
// Videos are looped here
public void startVideoLoop() {
playerIndex = 0;
videoLoop = () -> {
// Previous player MUST be stopped otherwise it is left
// in the PLAYING state and the next video will not PLAY
if (player != null) {
player.stop();
}
// Point to the next player
if (++playerIndex == videoPlayersList.size()) {
playerIndex = 0;
}
playVideoSequence();
};
playVideoSequence();
}
public void playVideoSequence() {
// If the video options are shown we can update the listview
if (playListViewController != null) {
playListViewController.setListViewItem(playerIndex);
}
player = videoPlayersList.get(playerIndex);
mediaView.setMediaPlayer(player);
player.setOnEndOfMedia(videoLoop);
player.play();
}
A right mouse button press then invokes a 'settings' option. This is a View Controller which is attached to an FXML view. The controller receives a reference to the videoField when it is invoked. Here is the relevant snippet:
private void setHandlers() {
idVolumeSlider.valueProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue arg0, Object arg1, Object arg2) {
// videoField is a separate class
videoField.setVolume(idVolumeSlider.getValue());
}
});
I'm sorry I cannot supply a 'cut and paste' example, because that would mean a lot of pruning and simplifying of classes to achieve that with the risk of introducing additional problems.