I've AdMob test ads fully working in my project.
When the ad is watched the below code should run, however only the first line is being executed, the 2 other lines are being ignored with no error.
public void HandleUserEarnedReward(object sender, Reward args)
{
GameControl.control.life += 1;
GameControl.control.Save();
txtLife.text = GameControl.control.life.ToString();
}
I've placed the same code in another method that I trigger with a button to test.
In this instance the full method is run.
public void runtest()
{
GameControl.control.life += 1;
GameControl.control.Save();
txtLife.text = GameControl.control.life.ToString();
}
If i'm not wrong you are trying to give reward after User watches rewarded video. I had the same problem one time, the problem is that functions that are executing inside of "HandleUserEarnedReward" will NOT be executed on unity's MainThread, but on Google's SDK thread.
There are several solutions:
https://github.com/PimDeWitte/UnityMainThreadDispatcher - Switch to main thread using this. Check readme file to more info.
Create global booleans with false value. Then on "HandleUserEarnedReward" change "isRewarded" boolean to true. Create Update function to check boolean value. Something like:
void Update()
{
if (isRewarded)
{
// do all the actions
// reward the player
isRewarded = false; // to make sure this action will happen only once.
}
}
Use Coroutine. Coroutines autoMagically switch to Unity's MainThread after "yield return"
public void HandleRewardBasedVideoRewarded(object sender, Reward args)
{
string type = args.Type;
double amount = args.Amount;
Debug.Log(
"HandleRewardBasedVideoRewarded event received for "
+ amount.ToString() + " " + type);
StartCoroutine(AfunctionName());
}
IEnumerator AfunctionName()
{
yield return new WaitForSecondsRealtime(0.1f);
// FB.LogAppEvent("AdmobRewardedView");
Debug.Log("Reward Function Called!!!!!");
GiveReward();
this.RequestRewardBasedVideo();
}
Related
Here, I am looking for a piece of code in the Unity editor that will execute the initial panel of the created package only once when Unity is opened, but this code is executed in the initial frame every time I play Unity and then stop it, and it continuously shows the panel. Gives. I want it to be seen only once when the project is opened.
[InitializeOnLoad]
public class Autorun
{
static Autorun()
{
EditorApplication.update += RunOnce;
}
static void RunOnce()
{
Debug.Log("Once"); // but it will repeat every time I Clicking on play then stop it.
Panel.Init();
EditorApplication.update -= RunOnce;
}
}
Above is the code suggested by the user in Unity Answers, which was chosen as the best answer, but in fact, as I pointed out, it had a problem.
Although it was mentioned in the comment that the second answer in Unity answers solves the problem, after a while I noticed that the panel opens again when saving the code and I could not find a way to block it through another condition in EditorApplication. The solution below is the final method of solving the problem that uses EditorPrefs and I feel it necessary to share this method with you.
internal const string FIRST_TIME = "FIRST_TIME"; // the key
static Autorun()
{
EditorApplication.update += RunOnce;
EditorApplication.quitting += Quit;
}
private static void Quit() => EditorPrefs.DeleteKey(FIRST_TIME);
private static void RunOnce()
{
var firstTime = EditorPrefs.GetBool(FIRST_TIME, true);
Debug.Log(firstTime);
if (firstTime)
{
EditorPrefs.SetBool(FIRST_TIME, false);
if (EditorPrefs.GetBool(Panel.ShowOnStart, true)) Panel.Init();
}
EditorApplication.update -= RunOnce;
}
}
I'm writing a chess UI with Unity as well as an external engine (executable written in c#). I can send and receive data to and from the process (engine). This is working, But when the Make_Move method is called, when the process responds back with data, a problem occurs. While debugging the code execution just stops when trying to access the Unity objects in the Make_Move method, and The objects are missing namely the piece gameObject and sprite but the rest of the variables(that are not unity objects) is still there. I don't get any errors, all of the variables are part of a class that is kept in an array that keeps track of the piece it's sprite the gameObject as well as other things.
why would only the unity objects disappear from the object array?
why does code execution stop when trying to access the unity objects (sprite's etc)?
How to fix this?
Unity class for sending and receiving a 4 digit string, the start, and end position of the move to make (xyxy)
public static class UCI
{
static Process process = new Process();
static UCI()
{
ProcessStartInfo si = new ProcessStartInfo()
{
FileName = "ChessEngine.exe",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
process.StartInfo = si;
process.OutputDataReceived += new DataReceivedEventHandler(OnRecieved);
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
}
public static void SendLine(string command)
{
Debug.Log("send: "+ command);
process.StandardInput.WriteLine(command);
process.StandardInput.Flush();
}
public static void Kill()
{
process.Kill();
}
private static void OnRecieved(object sender, DataReceivedEventArgs e)
{
string text = e.Data;
Debug.Log("Recieved: " + text);
if(text.Length == 4)
Game.Make_Move(new Move(Board.Squares[(int)char.GetNumericValue(text[0]),(int)char.GetNumericValue(text[1])],Board.Squares[(int)char.GetNumericValue(text[2]), (int)char.GetNumericValue(text[3])]));
}
}
Unity make_Move method where the problem occurs:
static void Make_Move(Move move)
{
var end = move.end.GetPiece();
//posistion is a vector2Int, this line does not cause any trouble
print("Pos: " + Squares[move.start.GetPosition().x, move.start.GetPosition().y].GetPosition());
//this line causes execution to stop without error. if it is commented out execution will continue.
print("obj: " + Squares[move.start.GetPosition().x, move.start.GetPosition().y].gameObject);
//lines below this is not executed
SetPiece(move.start.gameObject.GetComponent<Image>().sprite, move.end.GetPosition(), end.MoveCnt);
RemovePiece(move.start.GetPosition());
// set the current turn to the other player
CurrentPlayer = CurrentPlayer == players[0] ? CurrentPlayer = players[1] : CurrentPlayer = players[0];
}
main scene chess board image
(Most of) The Unity API can not be used by background threads!
I don't know your types you are using/storing in that Squares array but: be aware that most of the Unity API that directly influences or depends on the scene (e.g. any transform property, .gameObject etc) is only available to the Unity main thread! (The exception is e.g. simple Vector3 operations as long as you don't assign them to a Transform)
I'm pretty sure the issue here is that your OnReceived is executed on a different thread (the one of the started process) => You don't even see the exception in the main thread!
A pattern often used in Unity is a so called "Main Thread Dispatcher" and might look like e.g.
public class MainThreadDispatcher : MonoBehaviour
{
// Here we store the current existing instance of this component
private static MainThreadDispatcher _instance;
// Public read-only access
public static MainThreadDispatcher Instance => _instance;
// A thread-safe Queue (first-in/first-out) for the actions
// that shall be executed in the next Update call
private readonly ConcurrentQueue<Action> _actions = new ConcurrentQueue<Action>();
private void Awake ()
{
// Make sure only one instance of this exists (Singleton Pattern)
if(_instance && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
// Optional but recommended: Make sure this object is not
// destroyed if the scene is changed
DontDestroyOnLoad (gameObject);
}
private void Update ()
{
// Run all actions that have been enqueued since the last frame
while(_actions.TryDequeue(out var action)
{
action?.Invoke();
}
}
// Call this from any thread to make an action be executed in the next Update call
public void DoInMainThread(Action action)
{
// Simply add the action to the end of the thread-safe Queue
_actions.Enqueue(action);
}
}
Then you would in your class do
private static void OnRecieved(object sender, DataReceivedEventArgs e)
{
var text = e.Data;
Debug.Log("Recieved: " + text);
if(text.Length == 4)
{
MainThreadDispatcher.Instance.DoInMainThread(() =>
{
// Delay this code line until we are in the next unity main thread
// "Update" call where Unity API can be safely used
Game.MakeMove(new Move(Board.Squares[(int)char.GetNumericValue(text[0]),(int)char.GetNumericValue(text[1])],Board.Squares[(int)char.GetNumericValue(text[2]), (int)char.GetNumericValue(text[3])]));
});
}
}
Now all you have to do is make sure you have the MainThreadDispatcher component attached to any always active GameObject in your first scene.
I tried to do a video streaming application with Unity and I have successfully connect two user and are able to video chat & sharescreen. The problem is now when the other/second user press leave channel and rejoin again, first user will see the second user screen just stopped at the last frame. Only way to deal with it right now is to exit Unity play mode and re-enter again. Below are the code:
void LoadEngine()
{
print("Loading Engine...");
if (engine != null)
{
print("Engine already exists. Please unload it first!");
return;
}
engine = IRtcEngine.GetEngine(appId);
engine.SetLogFilter(LOG_FILTER.DEBUG);
}
void UnloadEngine()
{
print("Unloading Engine...");
if (engine != null)
{
IRtcEngine.Destroy();
engine = null;
}
}
void EnableVideo(bool yes)
{
engine.DisableAudio();
if (yes)
{
engine.EnableVideo();
engine.EnableVideoObserver();
}
else
{
engine.DisableVideo();
engine.DisableVideoObserver();
}
}
private void JoinChannel()
{
print("Joining Channel...");
EnableVideo(true);
// add our callback to handle Agora events
engine.OnJoinChannelSuccess += OnJoinChannelSuccess;
engine.OnUserJoined += OnUserJoined;
engine.OnUserOffline += OnUserLeave;
engine.OnLeaveChannel += OnLeaveChannel;
button_Join.onClick.RemoveListener(JoinChannel);
button_Join.onClick.AddListener(LeaveChannel);
button_Join.GetComponentInChildren<Text>().text = "Leave Channel";
if (string.IsNullOrEmpty(channelName))
{
return;
}
engine.JoinChannel(channelName, null, 0);
}
private void LeaveChannel()
{
print("Leaving Channel...");
button_Join.onClick.RemoveListener(LeaveChannel);
button_Join.onClick.AddListener(JoinChannel);
button_Join.GetComponentInChildren<Text>().text = "Join Channel";
playerOne.Clear();
playerTwo.Clear();
engine.LeaveChannel();
EnableVideo(false);
engine.OnJoinChannelSuccess -= OnJoinChannelSuccess;
engine.OnUserJoined -= OnUserJoined;
engine.OnUserOffline -= OnUserLeave;
engine.OnLeaveChannel -= OnLeaveChannel;
}
private void OnJoinChannelSuccess(string channelName, uint uid, int elapsed)
{
print("Joined with uid " + uid);
myId = uid;
playerOne.Set(0);
}
private void OnUserJoined(uint uid, int elapsed)
{
string userJoinedMessage = string.Format("onUserJoined callback uid {0} with elapsed {1}", uid, elapsed);
print(userJoinedMessage);
playerTwo.Set(uid);
}
private void OnUserLeave(uint uid, USER_OFFLINE_REASON reason)
{
string userLeaveMessage = string.Format("onUserOffline callback uid {0} with reason {1}", uid, reason);
print(userLeaveMessage);
playerTwo.Clear();
}
private void OnLeaveChannel(RtcStats stats)
{
playerTwo.Clear();
}
private void OnApplicationQuit()
{
UnloadEngine();
}
Is there anything I missed out in the code?
Edit
I suspect this is Agora API bugs where the video surface will not continue to stream the video when the UID was being changed in the runtime. This explained when the same user leave and rejoin, he will get different uid everytime he rejoin. The video surface will need to set UID to another number which causes the video surface stopped streaming.
Solution
I solved it using unity instantiate to real time instantiate the video surface when connected to channel and destroy the video surface game object when leaving a channel.
first of all, great name! ;)
I'm glad that you were able to work out a solution.
Another technique you can try, is calling:
yourVideoSurface.SetForUser(yourNewUID);
It looks like you're already implicitly doing that in your playerOne.Set(0) call, however I'd figure I'd mention it just in case.
I had the same issue in Unity. The tmp was returning -1. In the Update loop of VideoSurface.cs if this occurs, the update loop stops.
if (tmpi == -1)
return;
I agree with Jeremy, the Agora VideoSurface doesn't support changing users. I set the UID on the VideoSurface manually and it fixed the issue. I also agree that the sdk implies that it would change- which is misleading and frustrating.
I believe the issue may occur because VideoSurface.cs sets uint uid = mUid; in the update loop, so it is set to the default ID when the Unity scene starts then errors out when videoSurface.SetForUser(uid); is called by TestHelloUnityVideo when a user joins.
I'm making a system to balance calls inside of the OnGUI method of a EditorWindow.
I'm doing the following:
public void Update()
{
Repaint();
}
Inside of my OnGUI method I'm calling this Balancer. I have one list with the callbacks (List).
So the idea is simple, some callvaxc
I'm skipping some repaint frames for the callback that has the complete GUI, and calling on each repaint for other callbacks (for example, a marquee label or dispalying gifs).
By some reason, this error happens "Getting control 0's position in a group with only 0 controls when doing repaint"
private int m_repaintCounter;
public void Draw()
{
Event e = Event.current;
try
{
foreach (var action in m_actions)
{
try
{
// Test 1
// MainAction is a class that inherits from Action (class MainAction : Action)
if (action is MainAction)
{
bool isDesignedType = e.rawType == EventType.Repaint || e.rawType == EventType.Layout;
if (isDesignedType)
++m_repaintCounter;
if (!(m_repaintCounter == 20 && isDesignedType))
continue;
else
m_repaintCounter = 0;
}
// Test 2
action.Value();
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
}
catch
{
// Due to recompile the collection will modified, so we need to avoid the exception
}
}
But if I comment the "Test 1" everythings works fine.
On the ctor of the class we need to specify a callback to a GUI method, for example:
public Balancer(Action drawAction)
{
m_actions = new List<Action>();
m_actions.Add(drawAction);
}
So we could do easily (inside the EditorWindow):
private Balancer m_balancer;
public void OnEnable()
{
m_balancer = new Balancer(Draw);
}
public void Draw()
{
// This block will be called every 20 repaints as specified on the if statment
GUILayout.BeginHorizontal("box");
{
GUILayout.Button("I'm the first button");
GUILayout.Button("I'm to the right");
// This marquee will be called on each repaint
m_balancer.AddAction(() => CustomClass.DisplayMarquee("example"));
}
GUILayout.EndHorizontal();
}
// Inside of the Balancer class we have
// We use System.Linq.Expressions to identify actions that were added already
private HashSet<string> m_alreadyAddedActions = new HashSet<string>();
public void AddAction(Expression<Action> callback)
{
if(!m_alreadyAddedActions.Add(callback.ToString()))
return;
m_actions.Add(callback.Compile());
}
I can't figure this out. I couldn't find any information on the internet. Can anyone help me?
Ok, so, OnGui (IMGui) is awful to work with. If you aren't using it for an editor script, use the new 4.6 UI (UGui) instead.
Now then. The problem.
OnGui is called at least twice every frame. One of those is to calculate layouts and the other is to actually draw stuff ("repaint").
If the number of things, size of things, or anything else changes between these two calls then Unity will error with "Getting control 0's position in a group with only 0 controls when doing repaint."
That is: you cannot change UI state in the IMGui system at any point after Layout and before Repaint.
Only, only, only change state (and thereby which objects are being drawn) during Event.current == EventType.Repaint and only, only, only change state for the next frame (alternatively, do the changes during Event.current == EventType.Layout, provided that this same, new state will result in the same code path during Repaint). Do not, under any circumstances, make changes during Repaint that were not present during the previous Layout.
Trying to make a simple C# program where a ship goes through several "checkpoints" and when it's bounds interesect, it adds to the players score, then game ends when you reach the final checkpoint. Can't figure out how to make the score go up and print to the label each time. Thanks!
UPDATE: I can get my boxes to increase the score once, but not on all the other pictureboxes. Also, when I hit the final "spaceport" picturebox I get stuck in a messagebox redirect loop. How do I solve these 2 things? Tutors at school are no help.
public partial class consoleForm : Form
{
public consoleForm()
{
InitializeComponent();
}
private void consoleForm_Load(object sender, EventArgs e)
{
}
private void outputBox_TextChanged(object sender, EventArgs e)
{
}
private void timer1_Tick(object sender, EventArgs e)
{
int score = (0);
if (ship.Bounds.IntersectsWith(wormhole1.Bounds))
{
score += (1);
userScore.Text = score.ToString();
this.Refresh();
}
else if (ship.Bounds.IntersectsWith(wormhole2.Bounds))
{
score += (1);
userScore.Text = score.ToString();
this.Refresh();
}
else if (ship.Bounds.IntersectsWith(wormhole3.Bounds))
{
score += (1);
userScore.Text = score.ToString();
this.Refresh();
}
else if (ship.Bounds.IntersectsWith(wormhole4.Bounds))
{
score += (1);
userScore.Text = score.ToString();
this.Refresh();
}
if (ship.Bounds.IntersectsWith(spaceport.Bounds))
{
MessageBox.Show("you win");
this.Refresh();
}
}
The problem you've got is, that you're only doing this check once when the form loads, and never again.
Remove the logic from the consoleForm_Load event, and put it in your own method, called CheckScore(), or something else meaningful.
What would be good is to use a timer to check for the intersection every, let's say 100ms (0.1 seconds).
Create a timer:
In your constructor for the consoleForm, create a timer and a handler for it, then Start it. *You could even put it in your already-existing consoleForm_Load event - your choice :)
Like so:
public consoleForm()
{
var timer = new System.Timers.Timer(100); // Create a timer that fires every 100ms (0.1s)
timer.Tick += OnTimer_Tick;
timer.Start();
}
Add an event for the Tick event of the timer:
The OnTimer_Tick you can either "auto create" from VS, or add yourself:
private void OnTimer_Tick(object sender, ElapsedEventArgs e)
{
CheckScore(); // Call your logic method to check the score
}
Do the logic:
If you haven't already, make sure you've created the CheckScore() method using your original logic (that used to be in the consoleForm_Load event).
Final note:
I would seriously consider tidying up your CheckScore() (or whatever you like to call it) method, but that's just me :)
Further final note: there are loads of other ways of creating timers; I'm just being nice and using the most basic use of a Timer in WinForms :)
Hope this helps!
MORE!!!
At the moment, you're creating a new "score" every time you call the CheckScore() method.
In order to store your score, create a private field called _score or similar:
private int _score = 0;
Then, whenever you're adding to the user's score, reference the _score field, and use that to calculate and display:
_score++;
// or your own logic
_score += 20; // etc. etc.
// At the bottom of your logic,
// when you're ready to display the score:
userScore.Text = "Your score: " + _score;
Then, you can reference the _score wherever else you need it in your form.
Such as re-setting it:
private void ResetScore()
{
_score = 0;
}
or checking if the score has reached a certain value:
public void CheckScore()
{
...
// Your other logic to increment the score.
...
if (_score >= 10) // Check if the score has reached a certain value.
{
MessageBox.Show("You reached 10 points! YOU WIN!");
}
}
Bingo! ;)
Ok so your issue is that your only calling your code once - when the form is loaded. You need an update event set up and called regularly with the above code to make it work. Otherwise your code looks quite sound. Look into c# timers to get your update method called.