Setting Scale through RPC on Unity - networking

I'm having a difficult time getting my health bar to sync through the server on Unity using RPCs. In my game, the characters have health bars over their heads that are supposed to update for the entire server. That way you can just look at another player and see their health bar. The problem is that even though I send the information through the network and it's received, the actual physical bar doesn't change in scale. The player who sends the call has their bar changed though.
Here's a screenshot of the issue: http://i.imgur.com/g2GozZv.png
When I send the RPC, It does change the other player's health value, but does nothing to the scale.
I did the following code and it doesn't work:
void Start()
{
if(!networkView.isMine)
{
enabled = false;
}
}
void Update ()
{
if(Input.GetKey(KeyCode.Alpha2))
{
Minus_Health();
}
}
public void Minus_Health()
{
health -= 10;
healthBarLength = (float)health / (float)maxHealth / 5.1f;
healthBar.scale = new Vector2(healthBarLength, healthBar.scale.y);
Update_HP(health, maxHealth, healthBar.scale);
}
public void Update_HP(int hp, int maxHP, Vector3 bar)
{
networkView.RPC("Update_Health",RPCMode.All, hp, maxHP, bar);
}
[RPC]
public void Update_Health(int value, int value2, Vector3 bar)
{
health = value;
maxHealth = value2;
healthBar.scale = new Vector2(bar.x, bar.y);
}
I've also tried this, with no luck either:
void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
{
if (stream.isWriting)
{
Vector3 networkHealth = healthBar.scale;
stream.Serialize(ref networkHealth);
}
else
{
Vector3 networkHealth = Vector3.zero;
stream.Serialize(ref networkHealth);
healthBar.scale = networkHealth;
}
}

I found the problem. The code itself was working great (and you're right pek, that bar parameter was just tedious).
The problem was actually with the ex2D plugin I was using for the health bar. On each exSprite there is a camera view, set to the user's main camera. Because it was set to my camera on player instantiate, it was only seeing my bar through my camera, thus not updating the other bar via client / server side. By clicking on the texture and setting the ex2D exSprite's Camera to None, I can now see both bars being updated / scaled properly.
In hopes that this can help anyone looking for how to do health bars over a network, here is the final code I'm using:
using UnityEngine;
using System.Collections;
public class PlayerStats : MonoBehaviour
{
public int health;
public int maxHealth;
public float healthBarLength = 0;
public exSprite healthBar;
void Start()
{
if(!networkView.isMine)
{
enabled = false;
}
}
void Update ()
{
if(Input.GetKey(KeyCode.Alpha2))
{
Minus_Health();
Update_HP(health, maxHealth);
}
}
public void Minus_Health()
{
health -= 10;
}
public void Update_HP(int hp, int maxHP)
{
networkView.RPC("Update_Health", RPCMode.AllBuffered, hp, maxHP);
}
[RPC]
public void Update_Health(int value, int value2)
{
health = value;
maxHealth = value2;
healthBarLength = (float)value / (float)value2 / 5.1f;
healthBar.scale = new Vector2(healthBarLength, healthBar.scale.y);
}
}
Also, a small tip for those who get errors when setting the exSprite Camera to None; You need to update your ex2D plugin.
Thanks for the help pek and I hope this can help someone! :)

If all the parameters in Update Health are correct, then there might be something else that is affecting the scale.
Also, if value and value2 are sent correctly, then there is no need for the bar parameter:
[RPC]
public void Update_Health(int value, int value2)
{
health = value;
maxHealth = value2;
healthBarLength = (float)health / (float)maxHealth / 5.1f;
healthBar.scale = new Vector2(healthBarLength, healthBar.scale.y);
}

Related

Firebase Dynamic Links in Unity

Where should I put this chunk of code in order the listener function works every time I enter the app from the deep link?
Now in my unity mobile app I have this code in the initial load, however it does not work well.
The first case of entering the app from the deep link is not being handled. Only after initial load when I click the deep link my listener function works (as the listener is already set).
Is there any solution to this issue?
void Start()
{
DynamicLinks.DynamicLinkReceived += OnDynamicLink;
}
// Display the dynamic link received by the application.
void OnDynamicLink(object sender, EventArgs args)
{
var dynamicLinkEventArgs = args as ReceivedDynamicLinkEventArgs;
Debug.LogFormat("Received dynamic link {0}", dynamicLinkEventArgs.ReceivedDynamicLink.Url.OriginalString);
}
Test this, feel free to edit.
void Start()
{
StartCoroutine(WaitLoader()); //Loader
}
public void trueAwoken()
{
DynamicLinks.DynamicLinkReceived += OnDynamicLink;
}
public IEnumerator WaitLoader()
{
int i = 0;
while (i < 5) // Potential for true load state 5 (increase this 0-1000+?, it depends on your build?)
{
i++;
//Debug.Log("Waiting: " + i + "/" + Time.deltaTime);
yield return new WaitForFixedUpdate();
}
trueAwoken();
}

Retouching several images in several Task

Generalities : explanations about my program and its functioning
I am working on a photo-retouching JavaFX application. The final user can load several images. When he clicks on the button REVERSE, a Task is launched for each image using an Executor. Each of these Task executes the reversal algorithm : it fills an ArrayBlockingQueue<Pixel> (using add method).
When the final user clicks on the button REVERSE, as I said, these Task are launched. But just after these statements, I tell the JavaFX Application Thread to draw the Pixel of the ArrayBlockingQueue<Pixel> (using remove method).
Thus, there are parallelism and concurrency (solved by the ArrayBlockingQueue<Pixel>) between the JavaFX Application Thread and the Task, and between the Task themselves.
To draw the Pixel of the ArrayBlockingQueue<Pixel>, the JavaFX Application Thread starts an AnimationTimer. The latter contains the previously-mentionned remove method. This AnimationTimer is started for each image.
I think you're wondering yourself how this AnimationTimer can know to what image belongs the Pixel it has removed ? In fact, each Pixel has an attribute writable_image that specifies the image to what it belongs.
My problems
Tell me if I'm wrong, but my program should work. Indeed :
My JavaFX Application Thread is the only thread that change the GUI (and it's required in JavaFX) : the Task just do the calculations.
There is not concurrency, thanks to the BlockingQueue I use (in particular, there isn't possibility of draining).
The AnimationTimer knows to what image belongs each Pixel.
However, it's (obviously !) not the case (otherwise I wouldn't have created this question haha !).
My problem is that my JavaFX Application freezes (first problem), after having drawn only some reversed pixels (not all the pixels). On the last loaded image moreover (third problem).
A detail that could be the problems' cause
But I would need your opinion.
The AnimationTimer of course doesn't draw the reversed pixels of each image directly : this is animated. The final user can see each pixel of an image being reversed, little by little. It's very practical in other algorithms as the creation of a circle, because the user can "look" how the algorithm works.
But to do that, the AnimationTimer needs to read a variable called max. This variable is modified (writen) in... each Task. But it's an AtomicLong. So IF I AM NOT WRONG, there isn't any problem of concurrency between the Task themselves, or between the JavaFX Application Thread and these Task.
However, it could be the problem : indeed, the max's value could be 2000 in Task n°1 (= in image n°1), and 59 in Task n°2 (= in image n°2). The problem is the AnimationTimer must use 2000 for the image n°1, and 59 for the n°2. But if the Task n°1 et n°2 have finished, the only value known by the AnimationTimer would be 59...
Sources
When the user clicks on the button REVERSE
We launch the several Task and start several times the AnimationTimer. CLASS : RightPane.java
WritableImage current_writable_image;
for(int i = 0; i < this.gui.getArrayListImageViewsImpacted().size(); i++) {
current_writable_image = (WritableImage) this.gui.getArrayListImageViewsImpacted().get(i).getImage();
this.gui.getGraphicEngine().executor.execute(this.gui.getGraphicEngine().createTask(current_writable_image));
}
for(int i = 0; i < this.gui.getArrayListImageViewsImpacted().size(); i++) {
current_writable_image = (WritableImage) this.gui.getArrayListImageViewsImpacted().get(i).getImage();
this.gui.getImageAnimation().setWritableImage(current_writable_image);
this.gui.getImageAnimation().startAnimation();
}
The Task are part of the CLASS GraphicEngine, which contains an Executor :
public final Executor executor = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
public Task createTask(WritableImage writable_image) {
int image_width = (int) writable_image.getWidth(), image_height = (int) writable_image.getHeight();
Task ret = new Task() {
protected Void call() {
switch(operation_to_do) {
case "reverse" :
gui.getImageAnimation().setMax(image_width*image_height); // USE OF "MAX" VARIABLE
reverseImg(writable_image);
break;
}
return null;
}
};
return ret;
}
The same CLASS, GraphicEngine, also contains the reversal algorithm :
private void reverseImg(WritableImage writable_image) {
int image_width = (int) writable_image.getWidth(), image_height = (int) writable_image.getHeight();
BlockingQueue<Pixel> updates = gui.getUpdates();
PixelReader pixel_reader = writable_image.getPixelReader();
double[] rgb_reversed;
for (int x = 0; x < image_width; x++) {
for (int y = 0; y < image_height; y++) {
rgb_reversed = PhotoRetouchingFormulas.reverse(pixel_reader.getColor(x, y).getRed(), pixel_reader.getColor(x, y).getGreen(), pixel_reader.getColor(x, y).getBlue());
updates.add(new Pixel(x, y, Color.color(rgb_reversed[0], rgb_reversed[1], rgb_reversed[2], pixel_reader.getColor(x, y).getOpacity()), writable_image));
}
}
}
Finally, here is the code of the CLASS AnimationTimer. There is nothing particular. Note the variable max is used here too (and in the CLASS GraphicEngine : setMax).
public class ImageAnimation extends AnimationTimer {
private Gui gui;
private AtomicLong max, speed, max_delay;
private long count, start;
private WritableImage writable_image;
ImageAnimation (Gui gui) {
this.gui = gui;
this.count = 0;
this.start = -1;
this.max = new AtomicLong(Long.MAX_VALUE);
this.max_delay = new AtomicLong(999_000_000);
this.speed = new AtomicLong(this.max_delay.get());
}
public void setMax(long max) {
this.max.set(max);
}
public void setSpeed(long speed) { this.speed.set(speed); }
public double getMaxDelay() { return this.max_delay.get(); }
#Override
public void handle(long timestamp) {
if (start < 0) {
start = timestamp ;
return ;
}
ArrayList<Pixel> list_sorted_pixels = new ArrayList<>();
BlockingQueue<Pixel> updates = this.gui.getUpdates();
for(Pixel new_pixel : updates) {
if(new_pixel.getWritableImage() == writable_image) {
list_sorted_pixels.add(new_pixel);
}
}
while (list_sorted_pixels.size() > 0 && timestamp - start > (count * this.speed.get()) / (writable_image.getWidth()) && !updates.isEmpty()) {
Pixel update = list_sorted_pixels.remove(0);
updates.remove(update);
count++;
if (update.getX() >= 0 && update.getY() >= 0) {
writable_image.getPixelWriter().setColor(update.getX(), update.getY(), update.getColor());
}
}
if (count >= max.get()) {
this.count = 0;
this.start = -1;
this.max.set(Long.MAX_VALUE);
stop();
}
}
public void setWritableImage(WritableImage writable_image) { this.writable_image = writable_image; }
public void startAnimation() {
this.start();
}
}

CodenameOne filter optimization on set of containers

In my app, I have a searchbox which allows users to filter as they type. For some reason I can't get an InfinteProgress to properly display while the filtering is being executed.
Here's my code:
Pass 1
public void renderForumList(){
try{
magnify = mStateMachine.findForumSearchIcon(form);
}catch(NullPointerException ex){
System.out.println("User typed additional character in search term before previous term finished executing");
}
InfiniteProgress infi = new InfiniteProgress();
magnify.getParent().replace(magnify, infi, null);
Display.getInstance().invokeAndBlock(new Runnable() {
#Override
public void run() {
for (int i = 0;i < containerStates.length;i++){
if(containerStates[i] != listItems[i].isVisible()){
listItems[i].setHidden(!containerStates[i]);
listItems[i].setVisible(containerStates[i]);
}
}
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
mStateMachine.findForumsListComponent(form).animateLayout(200);
mStateMachine.findContainer2(form).replace(infi, magnify, null);
}
});
}
});
}
In this version, the infinite progress shows up in the proper position, but it doesn't spin.
Pass 2
public void renderForumList(){
try{
magnify = mStateMachine.findForumSearchIcon(form);
}catch(NullPointerException ex){
System.out.println("User typed additional character in search term before previous term finished executing");
}
InfiniteProgress infi = new InfiniteProgress();
magnify.getParent().replace(magnify, infi, null);
for (int i = 0;i < containerStates.length;i++){
if(containerStates[i] != listItems[i].isVisible()){
listItems[i].setHidden(!containerStates[i]);
listItems[i].setVisible(containerStates[i]);
}
}
mStateMachine.findForumsListComponent(form).animateLayout(200);
mStateMachine.findContainer2(form).replace(infi, magnify, null);
}
}
}
In this version, the magnifier icon just flashes briefly, but the InfiniteProgress spinner is never visible.
I get the same results on the simulator and on an Android device.
How can I get the InfiniteProgress to spin while the search is taking place?
invokeAndBlock opens a new thread and thus violates the EDT as you access UI components on a separate thread.
Try using callSerially instead to postpone the following code into the next EDT cycle although I'm not sure that will help as everything is still happening on the EDT.
Alternatively I'm guessing the method isVisible takes time, so you can enclose that call alone in invokeAndBlock.
To understand invokeAndBlock check out the developer guide https://www.codenameone.com/manual/edt.html

change text based on button-click

I am using Unity3d
I've tried to track where the problem comes from and had no success. The text update in "Guess" occurs properly when done by key press, but no by clicking the button (both log in an exception).
The code:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class NumberWizard : MonoBehaviour
{
public Text guessText;
int min;
int max;
int guess;
public void Higher ()
{
min = guess;
NextGuess ();
}
public void Lower ()
{
max = guess;
NextGuess ();
}
public void Correct ()
{
print ("I won!");
StartGame ();
}
private void StartGame ()
{
min = 1;
max = 1000;
guess = 500;
guessText.text = guess.ToString();
print (guess);
}
private void NextGuess ()
{
guess = (max + min) / 2;
guessText.text = guess.ToString();
print (guess);
}
// Use this for initialization
void Start ()
{
StartGame ();
}
// Update is called once per frame
void Update ()
{
if (Input.GetKeyDown (KeyCode.UpArrow)) {
Higher ();
} else if (Input.GetKeyDown (KeyCode.DownArrow)) {
Lower ();
} else if (Input.GetKeyDown (KeyCode.Return)) {
Correct ();
}
}
}
By the way, I've put the buttons on another controller called "LevelManager". I've added [RequireComponent(typeof(LevelManager))] above class declaration, but it didn't work.
Basically, it says that in guessText.text = guess.ToString(); object is not set to an instance, but I've set in Unity3d that the particular text is referenced and that it should use NumberWizard.cs
When you create this script it need to be attached to a GameObject. As you have done the coding it should be attached to an Empty gameObject. Then when you select the script from editor your public variable Text guessText will be available on the edior, Now you have to drag and drop the GUIText you have created on to the guessText property.
The error says that you have to initialize the variable, whether by doing a "new" or asingning it to a ingame drag'n'drop object as Dinal says.

Lucene number of occurrences

I am using Lucene.net in my Web App.
Everithing works fine, but now i have to show the number of occurrences of my 'searchstring' in every single document of the hits array.
How can i do this? I use usual BooleanQuery.
That is my search:
BooleanQuery bq = new BooleanQuery();
bq.Add(QueryParser.Parse(Lquery, "", CurIndexDescritor.GetLangAnalizer()), false,false);
BooleanQuery.SetMaxClauseCount(int.MaxValue);
IndexSearcher searcher = new IndexSearcher(indexPath);
Hits hits = (filter != null) ? searcher.Search(bq, filter) : searcher.Search(bq);
for (int i = 0; i < hits.Length(); i++)
{
Document doc = hits.Doc(i);
SearchResultItem MyDb = new SearchResultItem();
MyDb.key = doc.Get(KeyField);
MyDb.score = hits.Score(i);
result.Add(MyDb);
}
Where can i get the number of occurrences?
Thanks!
If you dont want the score back and dont want to order the results using score you could probably build a custom Similarity implementation.
I quickly tested the following code, and it appears to work fine with TermQueries and PhraseQueries, i didnt test more query types tho. A PhraseQuery hit counts as a single occurence.
public class OccurenceSimilarity : DefaultSimilarity
{
public override float Tf(float freq)
{
return freq;
}
public override float Idf(int docFreq, int numDocs)
{
return 1;
}
public override float Coord(int overlap, int maxOverlap)
{
return 1;
}
public override float QueryNorm(float sumOfSquaredWeights)
{
return 1;
}
public override Explanation.IDFExplanation idfExplain(System.Collections.ICollection terms, Searcher searcher)
{
return CACHED_IDF_EXPLAIN;
}
public override Explanation.IDFExplanation IdfExplain(Term term, Searcher searcher)
{
return CACHED_IDF_EXPLAIN;
}
public override float SloppyFreq(int distance)
{
return 1;
}
private static Explanation.IDFExplanation CACHED_IDF_EXPLAIN = new ExplainIt();
private class ExplainIt : Explanation.IDFExplanation
{
public override string Explain()
{
return "1";
}
public override float GetIdf()
{
return 1.0f;
}
}
}
To use it:
Similarity.SetDefault(new OccurenceSimilarity());

Resources