Unity - When does the frameCount goes negative - unity3d

I'm learning unity scripting. But I don't understand the following :
static private var lastRecalculation = -1;
static function RecalculateValue () {
if (lastRecalculation == Time.frameCount)
return;
ProcessData.AndDoSomeCalculations();
}
I don't get the third line or the condition in the IF statement. I know its a bit amateur, but pls help.
Thank You.

This is from the Time.frameCount documentation. This example shows how to create a function that executes only once per frame, regardless of how many objects you attach your script to. I suspect though that this example is incomplete because it never updates lastRecalculation (or it was assumed you would do so in AndDoSomeCalculations() ).
The reason setting lastRecalculation = -1 initially is so this function runs during the first frame.
Working version:
static var lastRecalculation = -1;
static function RecalculateValue () {
if (lastRecalculation == Time.frameCount) {
return;
}
lastRecalculation = Time.frameCount;
Debug.Log (Time.frameCount);
//ProcessData.AndDoSomeCalculations();
}
function Update () {
RecalculateValue();
}
Attach this script to 2 different GameObjects and run it. You will only see unique frame values 1,2,3,4,5.... even though 2 GameObjects are each calling RecalculateValue()
Now comment out the return and run it again. Now the ProcessData portion of that code runs for both objects every frame and you'll see something like 1,1,2,2,3,3,4,4......

Related

Part of my PlayerExpChangeEvent is being overridden by vanilla

I'm making a spigot plugin (version 1.8.8) that has an function that I know works because it fires flawlessly through my command. However, when I call it at the end of a PlayerExpChangeEvent, it seems like vanilla leveling overrides the bar, making it go up way more that it is supposed to. Running the command/function after this happens makes the bar go back to how it is supposed to be. I've tried setting my event's priority to highest (and when that didn't work, to lowest) but no matter what my function appears to be completely ignored when called inside the event.
Here is some code:
#EventHandler(priority=EventPriority.HIGHEST)
public void onXpGain(PlayerExpChangeEvent event)
{
// Load custom levels from config
ArrayList<String> levelList = new ArrayList<String>(plugin.getConfig().getStringList("levels"));
if (!((String)levelList.get(0)).equals("none"))
{
Player player = event.getPlayer();
Iterator<String> var4 = levelList.iterator();
while (var4.hasNext())
{
String s = (String)var4.next();
String[] splits = s.split(" ");
int levelCompare = Integer.parseInt(splits[0]);
int playerLvl = player.getLevel();
// Detect if on correct tier, else continue iteration
if (playerLvl == levelCompare - 1)
{
// Calculate the player's new XP amount
int totalXp = player.getTotalExperience() + event.getAmount();
player.setTotalExperience(totalXp);
updateBar(event.getPlayer()); // <-- THIS IS THE FUNCTION
return;
}
}
// At max level
player.setTotalExperience(player.getTotalExperience() + event.getAmount());
player.setLevel(getHighestLevel(levelList));
player.setExp(1.0f);
}
}
And here is the function itself. Keep in mind that it works fine when called through a command and not an event. It's purpose is to use the player's total XP to set the level and bar. Neither set correctly in the event; it instead embraces vanilla leveling.
public static void updateBar(Player player) {
ArrayList<String> levelList = new ArrayList<String>(plugin.getConfig().getStringList("levels"));
int totalXp = player.getTotalExperience();
player.setLevel(getHighestLevelForXp(totalXp, levelList));
if (player.getLevel() < getHighestLevel(levelList)) {
int lvlDiff = getTotalXpToLevel(player.getLevel() + 1,levelList) - getTotalXpToLevel(player.getLevel(),levelList);
int xpDiff = totalXp - getTotalXpToLevel(player.getLevel(),levelList);
player.setExp((float)xpDiff/lvlDiff);
} else {
player.setLevel(getHighestLevel(levelList));
player.setExp(0.0f);
}
return;
}
The command where the function works correctly is a bare-bones call to the function and doesn't need a mention here. Does anyone know how to get my event to override vanilla xp gain? The update works through the command, just not natural xp gain. It is already confirmed that the event DOES fire, as the rest of the event changes the internal xp amount, but the visual effects are overridden by vanilla. Can anyone help? Thanks.
Only setting the Player's EXP won't be enough for your desired behaviour. The Vanilla behaviour will still complete, as you're not changing how the event will add EXP to the player.
Currently, your event is working like this:
And PlayerExpGainEvent isn't cancellable, so you cannot undo it's addition of EXP.
What you can do instead is to set the EXP the event will add to 0, therefore not changing the player's EXP after your interception.
event.setAmount(0); //Cancelling the EXP addition
I would recommend to set your event to a high priority, so that other events that depend on Experience gain won't trigger when you set the amount gained to 0.

GetAssetPreview always returns null for UnityEngine.UI.Image

I have an array of prefabs i want to show a preview of in my custom editor. This works for gameobjects with a mesh renderer, for example the basic quad. However when i try to use AssetPreview.GetAssetPreview(tilePrefab.gameObject); on gameobject with a UnityEngine.UI.Image and a canvas renderer it always returns null.
Below is the part of the code that draws the previews.
public class MapEditor : Editor
{
public override void OnInspectorGUI()
{
for (int prefabIndex = 0; prefabIndex < TileSet.TilePrefabs.Count; prefabIndex++)
DrawIconTexture(prefabIndex, columnCount);
}
private void DrawIconTexture(int prefabIndex, int columnCount)
{
TileBehaviour tilePrefab = TileSet.TilePrefabs[prefabIndex];
Texture iconTexture = AssetPreview.GetAssetPreview(tilePrefab.gameObject);
Rect iconRect = GetIconRect(prefabIndex, columnCount);
if (iconTexture != null)
GUI.DrawTexture(iconRect, iconTexture);
else if (AssetPreview.IsLoadingAssetPreview(tilePrefab.gameObject.GetInstanceID()))
Repaint();
}
}
I know GetAssetPreview loads assets async, that is solved by the repaint. I have also tried
while(iconTexture == null)
iconTexture = AssetPreview.GetAssetPreview(tilePrefab.gameObject);
But that never finishes.
I also tried to use the texture of the Image
if (iconTexture == null)
iconTexture = tilePrefab.GetComponent<Image>().sprite.texture;
But that does not work because the sprite is in an atlas and all of the atlas is shown.
Edit: misread the question. I actually tried to use IsLoadingAssetPreview and IsLoadingAssetPreviews for few hours, without success. I ended up using a sad little trick
if (_previews.All(pair => pair.Value != null)) return;
_previews = GeneratePreviews();
I put that in the Update() loop of my EditorWindow. It's pretty hacky, I'm going to ask Unity if the AssetPreview methods are actually working.
Old answer, irrelevant:
You are not using the while loop and GetAssetPreview correctly.
GetAssetPreview will launch an asynchronous loading of the preview. To know if the preview is fully loaded you need to call AssetPreview.IsLoadingAssetPreview.
A pretty simple and brutal way of doing it (it will block execution) is :
var preview = AssetPreview.GetAssetPreview(item);
while (AssetPreview.IsLoadingAssetPreview(item.GetInstanceID())) {
// Loading
}
As usual, careful with while loops. Note that there is also a AssetPreview.IsLoadingAssetPreviews method with no parameters.
you should just use "using UnityEditor"

Unity3D - GameControl always null?

I don't know what is wrong with my destroy portion of the below code. I'm trying to have only 1 GameControl persist throughout the scenes. It seems that every time I switch back to this scene, my GameControl is read as null and a new GameControl is generated and I end up having more than 1 GameControl persisting.
For reference: I tried to port the code from 18:24 to JavaScript from this link
Please advise.
#pragma strict
var control : GameControl;
function Awake () {
Debug.Log("GameControl runs");
if (control == null)
{
DontDestroyOnLoad(gameObject);
control = this;
Debug.Log(control);
} else if (control != this)
{
Destroy(gameObject);
Debug.Log("Destroy?");
}
}
function Update ()
{
}
The problem is that you're not using a static variable like this:
static var control : GameControl;
In simple terms being static means this variable will hold the same value across all your scripts thus the check if(control == null) will always remain true because your GameObject will create a new variable when you load a new scene. This new object is still not static and is therefore completely different. I hope this helps. If you are still confused I'd advise reading some single pattern articles because there are many ways to achieve it other than this.

Unityscript transfer boolean value between functions

I'm having a bit of trouble with some Unityscript.
What I want to do is have a GUI message appear when certain objects are touched (then vanish after a time). I think I have it mostly worked out, but the message trips automatically.
My attempted solution is to have a conditional part of the GUI message that only allows it to appear when a boolean is true. Then in a different script that is already tripped when the object is touched, the boolean is set to true, so the script can run, and reset the boolean to false.
However I'm getting a "You can only call GUI functions from inside OnGUI. I'm not sure what that means.
Message code:
youdied.js
static var deathMessageShow : boolean = false;
function OnGUI() {
if(deathMessageShow == true){
if(Time.time >= 5 )
GUI.Box(Rect(200,300,275,150),"You Died");
}
deathMessageShow = false;
}
Other code (truncated):
dead.js
function OnTriggerEnter()
{
//code that resets environment
youdied.deathMessageShow = true;
}
Any suggestions on what is going on, or a better solution would be greatly appreciated.
The following code will show GUI message for 5 seconds then hides it:
youdied.js
static var deathMessageShow : boolean = false;
function OnGUI()
{
if(deathMessageShow)
{
GUI.Box(Rect(200,300,275,150),"You Died");
// call the function HideDeathMessage() after five seconds
Invoke("HideDeathMessage", 5.0f);
}
}
function HideDeathMessage()
{
deathMessageShow = false;
}
Other code:
dead.js
function OnTriggerEnter()
{
//code that resets environment
youdied.deathMessageShow = true;
}

Call function from another script

I'm programming a simple game (the first one I do on my own) in which basically there are two scoring goals, and when a player scores, I want to reset the position for all players to some coordinates.
I have a script attached to the goals, which detects collision with the ball, as follows:
Goal.js
#pragma strict
function OnTriggerEnter2D (hitInfo : Collider2D) {
if (hitInfo.name == "Ball")
{
var wallName = transform.name;
GameManager.Score (wallName);
hitInfo.gameObject.SendMessage ("ResetBall");
//Here I need to call the ResetPlayer function
PlayerControlHS.ResetPlayer();
}
}
The following script is attached to the players
PlayerControlHS.js
#pragma strict
var resetPosX : float;
var resetPosY : float;
//keys
var moveUp : KeyCode;
var moveLeft : KeyCode;
var moveRight : KeyCode;
var speed : int = 4;
function Update () {
if (Input.GetKey(moveUp)) {
rigidbody2D.velocity.y = speed;
}
if (Input.GetKey(moveLeft)) {
rigidbody2D.velocity.x = -speed;
}
else if (Input.GetKey(moveRight)) {
rigidbody2D.velocity.x = speed;
}
else {
rigidbody2D.velocity.x = 0;
}
}
function ResetPlayer () {
Debug.Log("I'm being called");
}
Both the goals and the players have a RigidBody 2D and a collider of some kind, as well as the ball.
I've gotten to get the function called as it is right now, but if I try to modify the position coordinates of a player, I enter a loop of death errors that when I fix one, I get another one.
That happens when I put this code in ResetPlayer():
rigidbody2D.position.x = resetPosX;
rigidbody2D.position.y = resetPosY;
Mostly, the errors are because I need an object of type PlayerControlHS to access those fields. I've tried adding a variable of that type (and referencing it to each player) but it doesn't work because it tells me I need an object to access that variable... I don't know how to initialize it if I make it static.
How could I get it working?
EDIT: I'm thinking the best approach would be sending a message just like the ResetBall one, but it doesn't work (I think because the function is called from ball, object of another type and it can't find the function). This way, the function ResetPlayer could stop being static. But I'm not sure if this is right, since I can't get it without compile errors.
EDIT2: It worked, even though I had to put it one liner
hitInfo.gameObject.GetComponent(PlayerControlHS).ResetPlayer();
Because Unity was telling me that I needed to put a semicolon here (I don't understand :S):
PlayerControlHS playerScript ; = hitInfo.gameObject.getComponent(PlayerControlHS);
But I still can't get to modify the variables of the position...
I have tried fixing it but I get an error saying that "I need an instance of type UnityEngine.Component to access non static member 'rigidBody2D' in a variable declaration I made
static var test : PlayerControlHS = rigidbody2D.GetComponent(PlayerControlHS);
And the code I'm trying in ResetPlayer:
test.GetComponent(PlayerControlHS).rigidbody2D.position.x = test.GetComponent(PlayerControlHS).resetPosX;
test.GetComponent(PlayerControlHS).rigidbody2D.position.y = test.GetComponent(PlayerControlHS).resetPosY;
Try changing this line:
PlayerControlHS.ResetPlayer();
To this:
PlayerControlHS playerScript = hitInfo.gameObject.getComponent(PlayerControlHS);
playerScript.ResetPlayer();
That is how you call a function from another script.
EDIT 1:
Sorry I got mixed up with C# even though you are using javascript, I figured out why you got that error. There's a slight difference in variable declaration.
Change the above code to this:
var playerScript:PlayerControlHS = hitInfo.gameObject.GetComponent(PlayerControlHS);
playerScript.ResetPlayer();
As for resetting the player's position, the script is inside the player game object is it not? Then you can just simplify it like this:
function ResetPlayer () {
rigidbody2D.position.x = resetPosX;
rigidbody2D.position.y = resetPosY;
}
Really sorry for that :(