Unity Scripting global variable - javascript

I have a script called Death which re spawns the player at the beginning location when the collision is true. I am trying to make a score count that when this collision is true it will minus 100 points but have been unsuccessful. The script bellow if from the score and death script. Any help would be much appreciated.
Score script:
var gui : GameObject;
static var score : int;
Death.death = false;
function Start ()
{
gui.GetComponent ("GUIText").text = "Score: 0";
}
function Update ()
{
gui.GetComponent ("GUIText").text = "Score: " + score;
if (death)
{
score = score - 100;
}
}
Death Script:
#pragma strict
var Ball : Transform;
public var death : boolean = false;
function OnCollisionEnter (b : Collision)
{
if (b.gameObject.tag == "Ball")
{
death = true;
Ball.transform.position.x = 1.6;
Ball.transform.position.y = 1.5;
Ball.transform.position.z = 1.1;
Ball.GetComponent.<Rigidbody>().velocity.y = 0;
Ball.GetComponent.<Rigidbody>().velocity.x = 0;
Ball.GetComponent.<Rigidbody>().velocity.z = 0;
}
}

I hope, that I can help you even though I'm using C#. It should be very easy to translate this to UnityScript.
using UnityEngine;
public class Score : MonoBehaviour
{
public GUIText guiText;
int score;
void Update()
{
if(DeathTrigger.wasTriggered)
{
DeathTrigger.wasTriggered = false;
score -= 100;
}
guiText.text = string.Format("Score: {0}", score);
}
}
public class DeathTrigger : MonoBehaviour
{
public static bool wasTriggered;
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Ball"))
{
wasTriggered = true;
// ...
}
}
}
I assume this is a beginner's questions, so I won't say anything about how static variables are evil and so on, but I still want to post an example of where to go next for a better approach:
using System;
using UnityEngine;
using UnityEngine.UI;
public class BetterDeathTrigger : MonoBehaviour
{
// This event will be called when death is triggered.
public static event Action wasTriggered;
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Ball"))
{
// Call the event.
if (wasTriggered != null)
wasTriggered();
// ...
}
}
}
public class BetterScore : MonoBehaviour
{
public Text uiText; // Unity 4.6 UI system
int score;
void Start()
{
// Subscribe to the event.
BetterDeathTrigger.wasTriggered += WasTriggered_Handler;
}
// This method will now be called everytime the event is called from the DeathTrigger.
private void WasTriggered_Handler()
{
score -= 100;
uiText.text = string.Format("Score: {0}", score);
}
}
A couple of things:
GUIText is pretty old and was already replaced by the new UI system since Unity version 4.6
Static variables are not smart in the long run, prefer instances of objects unless you are very sure how statics work
This is good example of where to use events. Again, it being static might lead to problems but for the first example it's the easiest.
The Unity Learn site offers a lot of tutorials about programming concepts such as "Communicating between scripts", they also have basic game examples where you can follow along with a complete project.

It's definitely worth trying what Xarbrough suggested to go with instead. Statics can be confusing and get in the way in the long run. But here's how you can do it, written in Javascript.
public class Death { // you can change the class name to something that is broad enough to hold several public static variables
public static var death : boolean;
}//This will end this class. When you make public classes like this, the script doesnt even need to be attached to a object, because it doesn't use Mono behavior
//This would be the actual DeathScript, whats above is technically not part of the Death script
var Ball : Transform;
function OnCollisionEnter (b : Collision) {
if (b.gameObject.tag == "Ball"){
Death.death = true;
Ball.transform.position.x = 1.6;
Ball.transform.position.y = 1.5;
Ball.transform.position.z = 1.1;
Ball.GetComponent.<Rigidbody>().velocity.y = 0;
Ball.GetComponent.<Rigidbody>().velocity.x = 0;
Ball.GetComponent.<Rigidbody>().velocity.z = 0;
} }
From there, anytime you want to access the static variable, just tell it where to look. Death.death.
Hope this helps!

Related

Tanks! Unity currently trying to add a health pick up item

void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag ("HealthPickUp"))
{
m_CurrentHealth += amount;
other.gameObject.SetActive (false);
SetHealthUI ();
}
if (m_CurrentHealth <= 0f && !m_Dead)
{
OnDeath ();
}
}
trying to get it that if the tank hits the pick up item it will gain 20 pointa but as of right now ive got no clue what to do
To get collisions working in Unity, you need to do the following:
Attach a Collider component to each object you want to be part of the collision. This can be a BoxCollider, SphereCollider, etc. Whatever shape makes sense.
For trigger collisions (i.e. non-physics collisions), enable the Is Trigger value in the inspector on each collider component you created in step 1.
Attach a RigidBody component to the object you want to be colliding into other things. RigidBody components are expensive, and you don't want them all over the scene, so I would suggest you put it on your Tank. You may also have to turn on the Is Kinematic property, otherwise your Tank may behave improperly (it's hard to tell with such little information in your setup).
Attach a script to your Tank that has implemented the method OnTriggerEnter. An example is below.
(Optional, strongly recommended) I would suggest you create another script and attach it to your health pickup. I will demonstrate with an example below.
(Optional) You can use layer based collisions to filter what kinds of collisions you detect to make it more efficient.
public class Tank : MonoBehaviour
{
// Setup the initial health values in the editor.
[SerializeField] private int _initialHealth = 100;
[SerializeField] private int _maxHealth = 100;
private int _health = -1;
private void Awake()
{
_health = _initialHealth;
}
private void Update()
{
// You probably want to check this in Update, since you want the tank
// to die as soon as it happens, not just when you pick up a piece of health.
if (_health <= 0)
{
OnDeath();
}
}
private void OnDeath()
{
// Your death logic.
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("HealthPickup"))
{
// Get the HealthPickup component and then add the amount to the tank's
// health. This is more robust because you can now create HealthPickup
// objects with a variable amount of health.
HealthPickup healthPickup = other.gameObject.GetComponent<HealthPickup>();
if (healthPickup != null)
{
_health = Mathf.Clamp(_health + healthPickup.amount, 0, _maxHealth);
healthPickup.OnPickup();
}
}
}
}
public class HealthPickup : MonoBehaviour
{
[SerializeField] private int _amount = default;
public int amount { get { return _amount; } }
public void OnPickup()
{
Destroy(gameObject);
}
}
If you follow these steps, you should have collisions working and a flexible system for giving your Tank health pickups.

Is there a workaround for Script Notify not working on UWP ms-appdata

I am developing an application for Xamarin.UWP which is trying to inject Javascript into a local html file (uri: ms-appdata:///local/index.html) like so:
async void OnWebViewNavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
if (args.IsSuccess)
{
// Inject JS script
if (Control != null && Element != null)
{
foreach (var f in Element.RegisteredFunctions.Where(ff => !ff.IsInjected))
{
await Control.InvokeScriptAsync("eval", new[] { string.Format(JavaScriptFunctionTemplate, f.Name) });
f.Injected();
}
}
}
}
Then when the Javascript method is called this will call the OnWebViewScriptNotify method so that I can proccess the request in my application.
The trouble is this doesnt work for some kind of security reasons:
This was a policy decision we made that we have had feedback on so we
re-evaluate it. The same restriction doesn't apply if you use
NavigateToStreamUri together with a resolver object. Internally that's
what happens with ms-appdata:/// anyway.
I then tried what is advised in this case which was to use a resolver as mentioned here: https://stackoverflow.com/a/18979635/2987066
But this has a massive affect on performance, because it is constantly converting all files to a stream to load in, as well as certain pages loading incorrectly.
I then looked at using the AddWebAllowedObject method like so:
private void Control_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
if (Control != null && Element != null)
{
foreach (var f in Element.RegisteredFunctions)
{
var communicator = new HtmlCommunicator(f);
Control.AddWebAllowedObject("HtmlCommunicator", communicator);
}
}
}
Where HtmlCommunicator is:
[AllowForWeb]
public sealed class HtmlCommunicator
{
public JSFunctionInjection Function { get; set; }
public HtmlCommunicator(JSFunctionInjection function)
{
Function = function;
}
public void Fred()
{
var d = 2;
//Do something with Function
}
}
and in my html it is like so:
try { window.HtmlCommunicator.Fred(); } catch (err) { }
But this doesn't work either.
So is there a way to work around this rediculous limitation?
So I found this answer: C# class attributes not accessible in Javascript
It says:
I believe you need to define the method name starting with a lower
case character.
For example: change GetIPAddress to getIPAddress.
I tested it on my side and found if I use the upper case name
'GetIPAddress', it won't work. But if I use getIPAddress, it works.
So I tried this:
I created a new project of type Windows Runtime Component as suggested here and I changed my method names to lower case so I had:
[AllowForWeb]
public sealed class HtmlCommunicator
{
public HtmlCommunicator()
{
}
public void fred()
{
var d = 2;
//Do something with Function
}
}
In my javascript I then had:
try { window.HtmlCommunicator.fred(); } catch (err) { }
and in my main UWP project I referenced the new Windows Runtime Component library and had the following:
public HtmlCommunicator communicator { get; set; }
private void Control_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
if (Control != null && Element != null)
{
communicator = new HtmlCommunicator();
Control.AddWebAllowedObject("HtmlCommunicator", communicator);
}
}
And this worked!

Assigning callback events from an array of strings (PIXI.js)

all. I have kind of a doozy of a problem, that could be solved really simply, if I just wanted to duplicate the code. I mean, really, it's a small part of a project that I'm doing just to see if I can, more than anything else, but it is bothering me since I've thought it up.
The Project
For fun, I've decided to take someone's ActionScript 3, text-based game engine and convert it to TypeScript and ultimately JavaScript using PixiJS.
The thing is, there are still 20213 errors to be fixed running tsc, so I could just leave this to a later date. But I was working on the Button class, which they defined as a subclass of MovieClip. That's fine; I just responded by reading up on PIXI buttons, and they seem fairly straightforward. Just, in the button's constructor, add something akin to the following lines:
export class Button extends PIXI.Sprite {
private _callback : Function;
private _height : number;
private _width : number;
public get callback() : Function { return this._callback; }
public set callback(fn : Function) {this._callback = fn; }
public get height() : number { return this._height; }
public set height(h : number) {this._height = h; }
public get width() : number {return this._width; }
public set width(w : number) {this._width = w; }
public constructor(width = 180, height = 90, callback: Function = null){
super(new PIXI.Texture(new PIXI.BaseTexture(GLOBAL.BTN_BACK, PIXI.SCALE_MODES.NEAREST)));
this.callback = callback;
this.width = width;
this.height = height;
this.buttonMode = true;
this.interactive = true;
this.anchor.set(0.5);
this.on('mousedown', this.callback)
.on('touchstart', this.callback);
}
}
That's a bit of a simplified version, and the version I did on Codepen uses a Container and a private _sprite field instead (as well as a ColorMatrixFilter that doesn't work too well on the black icons I picked out, but that's not really important for this question), but that's roughly the gist of how it's done.
The Problem
The problem is that, in the codepen, I'd like to do the following:
// assign `this.callback` to each of the following events:
let that = this;
['click','mousedown','touchstart'].map(evt => that.on(evt, that.callback});
with a simple call being passed in their constructors elsewhere:
for (let n = 0; n < 5; ++n){
btnArray.push(new Button(16, 16, () => console.info('You pushed button %d', n)));
}
but I'm not getting anything from them, even in the Chrome Console. I even logged that ColorMatrixFilter I mentioned earlier, to see if it was console.info that was wrong. Nope. So now, I'm confused on that. I was hoping to be able to just make a GLOBAL (a legacy static object from the AS source) key to iterate through for the events, but it looks like that's not happening.
The Questions
Is what I'm trying to do feasible, if odd? Is it blocked by a security feature (for which I'd be grateful)? If not, what am I doing wrong?
Should I even worry about setting all these different event handlers, or is just listening to click enough?
When an arrow function like your event map is executed the this context is not set, so any code that references this is going to get the current value, including any functions your map calls.
Replace your event map with the following:
['click','mousedown','touchstart'].map(function(evt) { that.on(evt, that.callback} } );
A demonstration:
function Named(x) {
this.name = x;
}
var foo = new Named("foo");
var bar = new Named("bar");
var showFunc = function show() {
// this is context dependant
console.log(this.name);
}
var showArrow;
// this is the window
showArrow = () => console.log(this.name);
var fooShowArrow;
(function() {
// this is foo
that = this;
fooShowArrow = () => console.log(that.name);
}).apply(foo);
var example = function(func) {
// For the demo, at this point, this will always be bar
func.apply(this, [ "arbitrary value" ]);
}
// explicitly set the current "this" to bar for the execution of these functions
example.apply(bar, [showFunc]); // works
example.apply(bar, [showArrow]); // fails, this is still the window
example.apply(bar, [fooShowArrow]); // fails, this is still foo

Access Static C# Variable from Javascript in Unity

I am trying to access C# static variable from javascript in unity 3D. How do i achieve it? I wanna get the score for me to use it in the javascript.
Score.cs
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Score : MonoBehaviour {
public Text txt;
public static int score;
public GameObject gameobj;
void Start () {
txt = gameobj.GetComponent<Text>();
txt.text = "Score : " + score;
}
void Update () {
txt = gameobj.GetComponent<Text> ();
txt.text = "Score : " + score;
score = score + 1;
}
int GetScore(){
return score;
}
}
Collide.js
#pragma strict
function OnTriggerEnter(otherObj: Collider){
var scoreScript : Score;
scoreScript = GetComponent("Score");
int score = scoreScript.GetScore();
if (otherObj.tag == "Wall"){
Debug.Log(score);
Application.LoadLevel(2);
}
else if (otherObj.tag == "End"){
Application.LoadLevel(6);
}
}
Let me tell you the answer in points:
C# code is compiled before JS code
in general, JS code can access C# classes, the opposite is not possible.
However, you can affect the order of compilation by moving scripts into special folders which are compiled earlier. You could move your JS script to a folder called "Plugins" then it works.
You can also see it (1) for more clarification while for detail guide of all steps available here (2)

Winforms equivalent of javascript setTimeout

Is there a simple solution/idea/strategy to create a setTimeout equivalent function in a WinForms app. I'm primarily a web developer but am not sure how I'd go about this in a WinForms App. Basically, I have a textbox, and after each keystroke I want to run a task to populate a list (like an auto-complete type thingy) but want to be able to cancel (e.g. clearTimeout) if the user keeps entering characters...
My only guess is to perhaps use a BackGroundWorker and make it sleep initially, and while it is sleeping, it could be cancelled, if the user stops entering keys and the sleep period ends, it then goes and runs the task etc
(i don't care if an example is C# or Vb.Net)
I know this is an old question but an alternative solution would be to use Task.Delay(delay).ContinueWith((task) => { /* Code */ });.
Thread.Sleep vs Task.Delay?
or there is await Task.Delay(delay);
https://social.msdn.microsoft.com/Forums/vstudio/en-US/345f0402-3af0-4f96-a501-073674883ba3/building-an-async-settimeout-function?forum=csharpgeneral
You can use a System.Timers.Timer: set AutoReset to false and use Start/Stop methods and create a handler for the Elapsed event.
Here's an example implementation in vb.net:
Public Sub SetTimeout(act As Action, timeout as Integer)
Dim aTimer As System.Timers.Timer
aTimer = New System.Timers.Timer(1)
' Hook up the Elapsed event for the timer.
AddHandler aTimer.Elapsed, Sub () act
aTimer.AutoReset = False
aTimer.Enabled = True
End Sub
public void setTimeout(Action TheAction, int Timeout)
{
Thread t = new Thread(
() =>
{
Thread.Sleep(Timeout);
TheAction.Invoke();
}
);
t.Start();
}
Timer Implementation:
public void SetTimeout(Action action, int timeout)
{
var timer = new System.Windows.Forms.Timer();
timer.Interval = timeout;
timer.Tick += delegate (object sender, EventArgs args)
{
action();
timer.Stop();
};
timer.Start();
}
I can propose following
internal class Timeout : System.Timers.Timer
{
public Timeout (Action action, double delay)
{
this.AutoReset = false;
this.Interval = delay;
this.Elapsed += (sender, args) => action();
this.Start();
}
}
// Example
var timeout = new Timeout(() => {
Console.WriteLine("init 1");
}, 500);
timeout.Stop();
You can use also:
Delay.Do(3000 /*in ms*/, () => { /* Do somthing */ });
Where Delay.Do is:
using System;
using System.Timers;
public class Delay
{
public static void Do(int after, Action action)
{
if (after <= 0 || action == null) return;
var timer = new Timer { Interval = after, Enabled = false };
timer.Elapsed += (sender, e) =>
{
timer.Stop();
action.Invoke();
timer.Dispose();
GC.SuppressFinalize(timer);
};
timer.Start();
}
}
Note: When updating a control in the UI thread use Control.Invoke:
Delay.Do(2000, () => { lblOk.Invoke((MethodInvoker)(() => { lblOk.Visible = false; })); });
This is my way, use C# 7.0 syntax feature.
Some differ with js, when timeout action execute then will can't be clear.
internal static class JsStyleTimeout
{
private static readonly ConcurrentDictionary<int, Thread> InnerDic;
private static int _handle;
static JsStyleTimeout()
{
InnerDic = new ConcurrentDictionary<int, Thread>();
}
public static int Set(Action action, int delayMs)
{
var handle = Interlocked.Increment(ref _handle);
var thread = new Thread(new ThreadStart(delegate
{
Thread.Sleep(delayMs);
InnerDic.TryRemove(handle, out var _);
Task.Factory.StartNew(action);
}));
InnerDic.TryAdd(handle, thread);
thread.Start();
return handle;
}
public static void Clear(int handle)
{
if (InnerDic.TryRemove(handle, out var thread))
thread.Abort();
}
}
public void setTimeout(Action act, int timeout)
{
Action action = () =>
{
Thread.Sleep(Timeout);
act();
};
new Thread(() => Invoke(action)).Start();
}
when using Task.Delay() and your action to edit/set winforms control. you have to add TaskScheduler.FromCurrentSynchronizationContext() or will get error Cross thread operation
void SetTimeout(Action action, int ms)
{
Task.Delay(ms).ContinueWith((task) =>
{
action();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
SetTimeout(() => {
myButton.Enabled = true;
}, 3000);
I'd recommend using reactive programming for this. See https://github.com/Reactive-Extensions/Rx.NET for the Reactive Extensions for .NET and http://reactivex.io/ for the general information about Reactive programming.
I'm afraid I'm only familiar with the JavaScript reactive library, so I can't give you a C-Sharp example, but in JavaScript it'd work something like this:
Rx.Observable.fromEvent(..eventdetails..)
.debounceTime(300)
.distinctUntilChanged()
.subscribe(eventHandler);
Using a setup like this you can chain operators to map and merge all kinds of events from a source to a subscriber. The simple example above reacts to an event, a keyUp for instance, and waits until there is no new keyUp for 300 ms and then calls the eventHandler, but only if the new value (after 300ms) is different from the last emitted value.

Categories