GetNode<Timer>("/root/Main/StartTimer") giving System.InvalidCastException - godot

I'm doing the "Your First 2D Game" in Godot because I'm a beginner. I used C# as my script language.
I'm having a problem with GetNode("/root/Main/StartTimer") giving to me a System.InvalidCastException: Specified cast is not valid.
But, StartTimer is the Timer type. So, There's no way it could launch this exception.
I'm exactly at this page of the tutorial:
https://docs.godotengine.org/en/stable/getting_started/first_2d_game/05.the_main_game_scene.html
public void New_Game()
{
Score = 0;
var player = GetNode<Player>("/root/Main/Player");
var startPosition = GetNode<Position2D>("/root/Main/StartPosition");
player.Start(startPosition.Position);
GetNode<Timer>("/root/Main/StartTimer").Start();
}
Well, I don't know what to do, since I'm following the tutorial. I think it shouldn't happen because StartTimer is the type Timer, screenshot below:
These are all my scripts:
Main.cs script:
using Godot;
using System;
public class Main : Node
{
// Declare member variables here. Examples:
// private int a = 2;
// private string b = "text";
#pragma warning disable 649
[Export]
public PackedScene MobScene;
#pragma warning restore 649
public int Score;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
GD.Randomize();
New_Game();
}
// // Called every frame. 'delta' is the elapsed time since the previous frame.
// public override void _Process(float delta)
// {
//
// }
public void Game_Over()
{
GetNode<Timer>("MobTimer").Stop();
GetNode<Timer>("ScoreTimer").Stop();
}
public void New_Game()
{
Score = 0;
var player = GetNode<Player>("/root/Main/Player");
var startPosition = GetNode<Position2D>("/root/Main/StartPosition");
player.Start(startPosition.Position);
GetNode<Timer>("/root/Main/StartTimer").Start();
//Timer StartTimer = GetNode<Timer>("/root/Main/StartTimer");
}
public void OnScoreTimerTimeout()
{
Score++;
}
public void OnStartTimerTimeout()
{
GetNode<Timer>("MobTimer").Start();
GetNode<Timer>("ScoreTimer").Start();
}
public void OnMobTimerTimeout()
{
//Create a new instance of the Mob Scene
var mob = (Mob)MobScene.Instance();
//Choose a random Location on Path2D
var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
mobSpawnLocation.Offset = GD.Randi();
//Set the mon's direction perpendicular to the path direction.
float direction = mobSpawnLocation.Rotation + Mathf.Pi / 2;
//Set the mob's position to a random location.
mob.Position = mobSpawnLocation.Position;
//Add some randomness to the direction
direction += (float)GD.RandRange(-Mathf.Pi / 4, Mathf.Pi / 4);
mob.Rotation = direction;
// Choose the velocity.
var velocity = new Vector2((float)GD.RandRange(150.0, 250.0), 0);
mob.LinearVelocity = velocity.Rotated(direction);
// Spawn the mob by adding it to the Main scene.
AddChild(mob);
}
}
Mobs.cs script:
using Godot;
using System;
public class Mob : RigidBody2D
{
// Declare member variables here. Examples:
// private int a = 2;
// private string b = "text";
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
var animSprite = GetNode<AnimatedSprite>("AnimatedSprite");
animSprite.Playing = true;
string[] mobTypes = animSprite.Frames.GetAnimationNames();
animSprite.Animation = mobTypes[GD.Randi() % mobTypes.Length];
}
// // Called every frame. 'delta' is the elapsed time since the previous
frame.
// public override void _Process(float delta)
// {
//
// }
public void OnVisibilityNotifier2DScreenExited()
{
QueueFree();
}
}
Player.cs script:
using Godot;
using System;
public class Player : Area2D
{
// Declare member variables here. Examples:
// private int a = 2;
// private string b = "text";
//How fast the player will move (pixel/sec).
[Export]
public int Speed = 400;
[Signal]
public delegate void Hit();
// Size of the game window.
public Vector2 ScreenSize;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
ScreenSize = GetViewportRect().Size;
Hide(); //Player is hidden when the game starts
}
// Called every frame. 'delta' is the elapsed time since the previous
frame.
public override void _Process(float delta)
{
var velocity = Vector2.Zero; //Tha Player's movement vector
if (Input.IsActionPressed("move_right"))
{
velocity.x += 1;
}
if (Input.IsActionPressed("move_left"))
{
velocity.x -= 1;
}
if (Input.IsActionPressed("move_down"))
{
velocity.y += 1;
}
if (Input.IsActionPressed("move_up"))
{
velocity.y -= 1;
}
var animatedSprite = GetNode<AnimatedSprite>("AnimatedSprite");
if (velocity.Length() > 0)
{
velocity = velocity.Normalized() * Speed;
animatedSprite.Play();
}
else
{
animatedSprite.Stop();
}
Position += velocity * delta;
Position = new Vector2(
x: Mathf.Clamp(Position.x, 0, ScreenSize.x),
y: Mathf.Clamp(Position.y, 0, ScreenSize.y)
);
if (velocity.x != 0)
{
animatedSprite.Animation = "walk";
animatedSprite.FlipV = false;
animatedSprite.FlipH = velocity.x < 0; //Here I'm doing a boolean
test
}
else if (velocity.y != 0)
{
animatedSprite.Animation = "up";
animatedSprite.FlipV = velocity.y > 0; //Here I'm doing a boolean
test
}
}
public void On_Player_Body_Entered(PhysicsBody2D body)
{
Hide(); //Player disappears after being hit.
EmitSignal(nameof(Hit));
//Must be deferred as we can't change physics properties on a physics
callback.
GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred("disabled",
true);
}
//Reset the player when starting the game
public void Start(Vector2 pos)
{
Position = pos;
Show();
GetNode<CollisionShape2D>("CollisionShape2D").Disabled = false;
}
}
If you can help me, please.. I'd be very glad and thankful if you could! Thank you!

The reason your game crashes is because of the main.cs script that is attached to all timer nodes.
Since the main.cs script extends the Node object all your timers are becoming Node objects instead of Timer objects. And because Node objects do not have a Start() function your game crashes.
The simple solution to this is to detach the main script from all nodes but the main node.
As Bugfish pointed out, Godot must not attach a script to a node when you connect a signal. Especially not a script from another node. After you deleted your main script from the timers, I recommend to go through the process of signal connection again just to figure out what went wrong the first time.

Related

Resize GameObject over time [duplicate]

I made a test game in unity that makes it so when I click on a button, it spawns a cylinder created from a factory class. I'm trying to make it so when I create the cylinder, its height shrinks over the next 20 seconds. Some methods I found are difficult to translate into what I'm doing. If you could lead me to the right direction, I'd very much appreciate it.
Here's my code for the cylinder class
public class Cylinder : Shape
{
public Cylinder()
{
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
cylinder.transform.position = new Vector3(3, 0, 0);
cylinder.transform.localScale = new Vector3(1.0f, Random.Range(1, 2)-1*Time.deltaTime, 1.0f);
cylinder.GetComponent<MeshRenderer>().material.color = Random.ColorHSV();
Destroy(cylinder, 30.0f);
}
}
This can be done with the Time.deltaTime and Vector3.Lerp in a coroutine function. Similar to Rotate GameObject over time and Move GameObject over time questions. Modified it a little bit to do just this.
bool isScaling = false;
IEnumerator scaleOverTime(Transform objectToScale, Vector3 toScale, float duration)
{
//Make sure there is only one instance of this function running
if (isScaling)
{
yield break; ///exit if this is still running
}
isScaling = true;
float counter = 0;
//Get the current scale of the object to be moved
Vector3 startScaleSize = objectToScale.localScale;
while (counter < duration)
{
counter += Time.deltaTime;
objectToScale.localScale = Vector3.Lerp(startScaleSize, toScale, counter / duration);
yield return null;
}
isScaling = false;
}
USAGE:
Will scale GameObject within 20 seconds:
StartCoroutine(scaleOverTime(cylinder.transform, new Vector3(0, 0, 90), 20f));
Check out Lerp. A general example of how to use it would be something like this:
float t = 0;
Update()
{
t += Time.deltaTime;
cylinder.localScale = new Vector3(1, Mathf.Lerp(2f, 1f, t/3f), 1); // shrink from 2 to 1 over 3 seconds;
}
You will create a new monobehaviour script and add it to your primitive. Then you wil use "Update" method of monobehaviour (or use coroutine) for change object over time.
Monobehaviour must be look like this:
public class ShrinkBehaviour : MonoBehaviour
{
bool isNeedToShrink;
Config currentConfig;
float startTime;
float totalDistance;
public void StartShrink(Config config)
{
startTime = Time.time;
currentConfig = config;
totalDistance = Vector3.Distance(currentConfig.startSize, currentConfig.destinationSize);
isNeedToShrink = true;
transform.localScale = config.startSize;
}
private void Update()
{
if (isNeedToShrink)
{
var nextSize = GetNextSize(currentConfig);
if (Vector3.Distance(nextSize, currentConfig.destinationSize) <= 0.05f)
{
isNeedToShrink = false;
return;
}
transform.localScale = nextSize;
}
}
Vector3 GetNextSize(Config config)
{
float timeCovered = (Time.time - startTime) / config.duration;
var result = Vector3.Lerp(config.startSize, config.destinationSize, timeCovered);
return result;
}
public struct Config
{
public float duration;
public Vector3 startSize;
public Vector3 destinationSize;
}
}
For using this, you must do next:
public Cylinder()
{
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
var shrink = cylinder.AddComponent<ShrinkBehaviour>();
shrink.StartShrink(new ShrinkBehaviour.Config() { startSize = Vector3.one * 10, destinationSize = Vector3.one * 1, duration = 20f });
cylinder.transform.position = new Vector3(3, 0, 0);
cylinder.GetComponent<MeshRenderer>().material.color = Random.ColorHSV();
Destroy(cylinder, 30.0f);
}
You must remember, monobehaviour-script must be in separate file, and must have name similar to monobehaviour-class name. For example, ShrinkBehaviour.cs;

Raycast help (using C#)

I have a raycast going upwards from an object which when the player comes in contact with the ray the object changes color. That works but I want to do it so when you touch the ray a second time, the object gets destroyed and I have no idea how to do that. I'm using Unity 2d.
Code: `using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyEnemy : MonoBehaviour //Enemy 3
{
[SerializeField] Transform Enemy3WayPoint;
private Renderer rend;
private Color colorToTurnTo = Color.blue;
void Start()
{
rend = GetComponent<Renderer>();
rend.enabled = true;
Physics2D.queriesStartInColliders = false;
}
private void Update()
{
RaycastHit2D hitInfo = Physics2D.Raycast(transform.position, Vector3.up, 5);
if (hitInfo.collider.gameObject.tag == "Player")
{
rend.material.color = colorToTurnTo;
Debug.DrawLine(transform.position, hitInfo.point, Color.white);
}`
There may be a bracket or two I forgot to include, it does work when I test it
I think the simplest solution is to use a variable to keep track of the number of times the ray has been hit by the player.
As for destroying the enemy, you can use the destroy function.
So, something like this:
int hitCount = 0; //add a class variable
void Update(){
RaycastHit2D hitInfo = Physics2D.Raycast(transform.position, Vector3.up, 5);
if (hitInfo.collider.gameObject.tag == "Player")
{
hitCount++;
}
if(hitCount == 1)
{
rend.material.color = colorToTurnTo;
Debug.DrawLine(transform.position, hitInfo.point, Color.white);
}
else if(hitCount >= 2)
{
Destroy(gameObject); //this will destroy the gameObject that the component is attached to
}
}
EDIT: It seems the OP's main problem was adding a delay to the events. Here is some updated code that addresses that problem:
bool waitingForFirstHit = true;
bool waitingForSecondHit = false;
float timeDelay = 1.5f;
void Update(){
RaycastHit2D hitInfo = Physics2D.Raycast(transform.position, Vector3.up, 5);
if (hitInfo.collider.gameObject.tag == "Player" )
{
if (waitingForFirstHit) {
ChangeColor();
waitingForFirstHit = false;
waitingForSecondHit = true;
}
else if(waitingForSecondHit && timeDelay < 0)
{
DestroyEnemy ();
}
}
if(waitingForSecondHit)
{
timeDelay -= Time.deltaTime;
}
}
void ChangeColor()
{
rend.material.color = colorToTurnTo;
Debug.DrawLine(transform.position, hitInfo.point, Color.white);
}
void DestroyEnemy()
{
Destroy(gameObject);
}
Here is a tutorial on using the Destroy function:
https://unity3d.com/learn/tutorials/topics/scripting/destroy
And here is a link to the docs:
https://docs.unity3d.com/ScriptReference/Object.Destroy.html
Cheers.

Undetected Null Pointer Exception

I have an error regarding Null Pointer Exception and I can't find the exact location of the error in my program. I have searched for probable solutions online but I find none of them helpful. I know what Null Pointer Exception is but I can't pinpoint what went wrong in my code. Any help will be appreciated, thanks.
Here's my code:
LevelOneScreen.java
public class LevelOneScreen implements Screen {
private final ThumbChase app;
WalkAnimate walkAnimate;
private Stage stage;
private Image levelOneImage;
private Image holdStartImage;
public Image walkRightImage;
public Image walkLeftImage;
public float deltaTime = Gdx.graphics.getDeltaTime();
public LevelOneScreen(final ThumbChase app){
this.app = app;
this.stage = new Stage(new StretchViewport(app.screenWidth,app.screenHeight , app.camera));
}
#Override
public void show() {
Gdx.input.setInputProcessor(stage);
walkAnimate = new WalkAnimate();
levelOneBackground();
holdStart();
ninjaWalk(); ==>ERROR1:LevelOneScreen.java:54
}
public void holdStart(){
Texture holdStartTexture = new Texture("HoldStart.png");
holdStartImage = new Image(holdStartTexture);
float holdStartImageW = holdStartImage.getWidth();
float holdStartImageH = holdStartImage.getHeight();
float holdStartImgWidth = app.screenWidth*0.8f;
float holdStartImgHeight = holdStartImgWidth *(holdStartImageH/holdStartImageW);
holdStartImage.isTouchable();
holdStartImage.setSize(holdStartImgWidth,holdStartImgHeight);
holdStartImage.setPosition(stage.getWidth()/2-holdStartImgWidth/2,stage.getHeight()/2-holdStartImgHeight/2);
stage.addActor(holdStartImage);
holdStartImage.addListener(new ActorGestureListener(){
public void touchDown (InputEvent event, float x, float y, int pointer, int button){
holdStartImage.setVisible(false);
};
});
}
public void levelOneBackground(){
Texture levelOneTexture = new Texture("BGBlue Resize.png");
levelOneImage = new Image(levelOneTexture);
levelOneImage.setSize(app.screenWidth,app.screenHeight);
levelOneImage.setPosition(0,0);
stage.addActor(levelOneImage);
levelOneImage.addListener(new ActorGestureListener(){
public void touchDown (InputEvent event, float x, float y, int pointer, int button){
holdStartImage.setVisible(false);
};
});
}
public void ninjaWalk(){
ERROR2==> TextureRegion ninjaWalkRight = new TextureRegion(walkAnimate.getCurrentFrameRight());
TextureRegion ninjaWalkLeft = new TextureRegion(walkAnimate.getCurrentFrameLeft());
walkRightImage = new Image(ninjaWalkRight);
walkLeftImage = new Image(ninjaWalkLeft);
float walkImageW = walkRightImage.getWidth();
float walkImageH = walkRightImage.getHeight();
float walkImageWidth = app.screenWidth*0.15f;
float walkImageHeight = walkImageWidth*(walkImageH/walkImageW);
walkLeftImage.isTouchable();
walkRightImage.isTouchable();
walkRightImage.setSize(walkImageWidth,walkImageHeight);
walkLeftImage.setSize(walkImageWidth,walkImageHeight);
walkRightImage.setPosition(stage.getWidth()/2-walkImageWidth/2,0);
walkLeftImage.setPosition(stage.getWidth()/2-walkImageWidth/2,0);
walkLeftImage.addAction(moveBy(3f,3f,3f));
stage.addActor(walkLeftImage);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
walkAnimate.update(deltaTime);
update(delta);
}
public void update(float delta){
stage.act(delta);
stage.draw();
app.batch.begin();
app.batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
stage.dispose();
}
}
WalkAnimate.java
public class WalkAnimate {
public ThumbChase app;
public Stage stage;
private Animation walkAnimationRight;
private Animation walkAnimationLeft;
private Texture walkSheetRight;
private Texture walkSheetLeft;
private TextureRegion[] walkFramesRight;
private TextureRegion[] walkFramesLeft;
private TextureRegion currentFrameRight;
private TextureRegion currentFrameLeft;
private float stateTime;
private Rectangle bound; //used for positioning and collision detection
private static final int FRAME_COLS_WALK = 3;
private static final int FRAME_ROWS_WALK= 2;
private float screenWidth = Gdx.graphics.getWidth();
private float screenHeight = Gdx.graphics.getHeight();
public float currentFrameWidth = (float)(screenHeight*0.15);
public float currentFrameHeight = (float)(screenHeight*0.15);
public float walkSheetWidth;
public float walkSheetHeight;
public WalkAnimate () {
walkSheetRight = new Texture("ninjaWalkRight.png");
walkSheetWidth = walkSheetRight.getWidth();
walkSheetHeight = walkSheetRight.getWidth();
TextureRegion[][] tmp = TextureRegion.split(walkSheetRight, (int) walkSheetRight.getWidth() / FRAME_COLS_WALK, (int) walkSheetRight.getHeight() / FRAME_ROWS_WALK);
walkFramesRight = new TextureRegion[FRAME_COLS_WALK * FRAME_ROWS_WALK];
int index = 0 ;
for (int i = 0; i < FRAME_ROWS_WALK; i++) {
for (int j = 0; j < FRAME_COLS_WALK; j++) {
walkFramesRight[index++] = tmp[i][j];
}
}
walkAnimationRight = new Animation(0.044f, walkFramesRight);
stateTime = 0f;
walkSheetLeft = new Texture("ninjaWalkLeft.png");
walkSheetWidth = walkSheetLeft.getWidth();
walkSheetHeight = walkSheetLeft.getWidth();
TextureRegion[][] tmp1 = TextureRegion.split(walkSheetLeft, (int) walkSheetRight.getWidth() / FRAME_COLS_WALK, (int)walkSheetLeft.getHeight() / FRAME_ROWS_WALK);
walkFramesLeft = new TextureRegion[FRAME_COLS_WALK * FRAME_ROWS_WALK];
int index1 = 0;
for (int i = 0; i < FRAME_ROWS_WALK; i++) {
for (int j = 0; j < FRAME_COLS_WALK; j++) {
walkFramesLeft[index1++] = tmp1 [i][j];
}
}
walkAnimationLeft = new Animation(0.044f, walkFramesLeft);
stateTime = 0f;
}
public Rectangle getBound(){
return bound;
}
public void update(float delta){
stateTime += delta;
currentFrameRight = walkAnimationRight.getKeyFrame(stateTime, true);
currentFrameLeft = walkAnimationLeft.getKeyFrame(stateTime, true);
}
public TextureRegion getCurrentFrameRight(){
return currentFrameRight;
}
public TextureRegion getCurrentFrameLeft(){
return currentFrameLeft;
}
}
And here's the error:
> java.lang.NullPointerException
> at
> com.badlogic.gdx.graphics.g2d.TextureRegion.setRegion(TextureRegion.java:112)
> at
> com.badlogic.gdx.graphics.g2d.TextureRegion.<init>(TextureRegion.java:63)
> at
> com.jpfalmazan.thumbchaseninja.GameScreens.LevelOneScreen.show(LevelOneScreen.java:54)
> at com.badlogic.gdx.Game.setScreen(Game.java:61)
> at
> com.jpfalmazan.thumbchaseninja.GameScreens.MenuScreen$1.clicked(MenuScreen.java:82)
> at
> com.badlogic.gdx.scenes.scene2d.utils.ClickListener.touchUp(ClickListener.java:89)
> at
> com.badlogic.gdx.scenes.scene2d.InputListener.handle(InputListener.java:58)
> at com.badlogic.gdx.scenes.scene2d.Stage.touchUp(Stage.java:353)
> at
> com.badlogic.gdx.backends.android.AndroidInput.processEvents(AndroidInput.java:379)
> at
> com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(AndroidGraphics.java:457)
> at
> android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1516)
> at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240)
you can find the exact line in logcat logs, you can see the activity name as well as the line of code and by clicking on it you will be redirected to the individual Activity on same line..
Hope this will help you.
One of your textures is either named wrong, or you are attempting to access it before it has been loaded.
You can tell by this exception;
com.badlogic.gdx.graphics.g2d.TextureRegion.setRegion(TextureRegion.java:112)
This mean that the texture region cannot map the texture identifier to a valid texture in your program, so either ninjaWalkRight or ninjaWalkLeft does not exist or is not loaded.
If you are using texture packer then ensure your files are called ninjaWalkRight and ninjaWalkLeft.
Also use the AssetManager to ensure these files are loaded before trying to access them as that may be an issue also.
Looks like you don't call walkAnimate.update(dt) before calling walkAnimate.getCurrentFrameRight() the first time, so walkAnimate.getCurrentFrameRight() returns null.
Moreover, in ninjaWalk() you create two actors (walkRightImage and walkLeftImage), only add one to the stage and never update the texture of it. You should rework your concept: Create one actor at startup, store it in a field and change the texture of it in your render/update method.

Run RichTextBox on its own thread?

Background
I have a RichTextBox control I am using essentially like a console in my WinForms application. Currently my application-wide logger posts messages using delegates and one of the listeners is this RTB. The logger synchronously sends lots of short (less than 100 char) strings denoting event calls, status messages, operation results, etc.
Posting lots of these short messages to the RTB using BeginInvoke provides UI responsiveness until heavy parallel processing starts logging lots of messages and then the UI starts posting items out of order, or the text is far behind (hundreds of milliseconds). I know this because when the processing slows down or is stopped, the console keeps writing for some time afterwords.
My temporary solution was to invoke the UI synchronously and add a blocking collection buffer. Basically taking the many small items from the Logger and combining them in a stringbuilder to be posted in aggregate to the RTB. The buffer posts items as they come if the UI can keep up, but if the queue gets too high, then it aggregates them and then posts to the UI. The RTB is thus updated piece-meal and looks jumpy when lots of things are being logged.
Question
How can I run a RichTextBox control on its own UI thread to keep other buttons on the same Form responsive during frequent but small append operations? From research, I think I need to run an STA thread and call Application.Run() on it to put the RTB on its own thread, but the examples I found lacked substantive code samples and there don't seem to be any tutorials (perhaps because what I want to do is ill advised?). Also I wasn't sure if there where any pitfalls for a single Control being on its own thread relative to the rest of the Form. (ie. Any issues closing the main form or will the STA thread for the RTB just die with the form closing? Any special disposing? etc.)
This should demonstrate the issue once you add 3 Buttons and a RichTextBox to the form. What I essentially want to accomplish is factoring away the BufferedConsumer by having the RTB on its own thread. Most of this code was hacked out verbatim from my main application, so yes, it is ugly.
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
// Fields
private int m_taskCounter;
private static CancellationTokenSource m_tokenSource;
private bool m_buffered = true;
private static readonly object m_syncObject = new object();
// Properties
public IMessageConsole Application_Console { get; private set; }
public BufferedConsumer<StringBuilder, string> Buffer { get; private set; }
public Form1()
{
InitializeComponent();
m_tokenSource = new CancellationTokenSource();
Application_Console = new RichTextBox_To_IMessageConsole(richTextBox1);
Buffer =
new BufferedConsumer<StringBuilder, string>(
p_name: "Console Buffer",
p_appendBuffer: (sb, s) => sb.Append(s),
p_postBuffer: (sb) => Application_Console.Append(sb));
button1.Text = "Start Producer";
button2.Text = "Stop All";
button3.Text = "Toggle Buffering";
button1.Click += (o, e) => StartProducerTask();
button2.Click += (o, e) => CancelAllProducers();
button3.Click += (o, e) => ToggleBufferedConsumer();
}
public void StartProducerTask()
{
var Token = m_tokenSource.Token;
Task
.Factory.StartNew(() =>
{
var ThreadID = Interlocked.Increment(ref m_taskCounter);
StringBuilder sb = new StringBuilder();
var Count = 0;
while (!Token.IsCancellationRequested)
{
Count++;
sb.Clear();
sb
.Append("ThreadID = ")
.Append(ThreadID.ToString("000"))
.Append(", Count = ")
.AppendLine(Count.ToString());
if (m_buffered)
Buffer
.AppendCollection(sb.ToString()); // ToString mimicks real world Logger passing strings and not stringbuilders
else
Application_Console.Append(sb);
Sleep.For(1000);
}
}, Token);
}
public static void CancelAllProducers()
{
lock (m_syncObject)
{
m_tokenSource.Cancel();
m_tokenSource = new CancellationTokenSource();
}
}
public void ToggleBufferedConsumer()
{
m_buffered = !m_buffered;
}
}
public interface IMessageConsole
{
// Methods
void Append(StringBuilder p_message);
}
// http://stackoverflow.com/a/5706085/1718702
public class RichTextBox_To_IMessageConsole : IMessageConsole
{
// Constants
private const int WM_USER = 0x400;
private const int WM_SETREDRAW = 0x000B;
private const int EM_GETEVENTMASK = WM_USER + 59;
private const int EM_SETEVENTMASK = WM_USER + 69;
private const int EM_GETSCROLLPOS = WM_USER + 221;
private const int EM_SETSCROLLPOS = WM_USER + 222;
//Imports
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, ref Point lParam);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, IntPtr lParam);
// Fields
private RichTextBox m_richTextBox;
private bool m_attachToBottom;
private Point m_scrollPoint;
private bool m_painting;
private IntPtr m_eventMask;
private int m_suspendIndex = 0;
private int m_suspendLength = 0;
public RichTextBox_To_IMessageConsole(RichTextBox p_richTextBox)
{
m_richTextBox = p_richTextBox;
var h = m_richTextBox.Handle;
m_painting = true;
m_richTextBox.DoubleClick += RichTextBox_DoubleClick;
m_richTextBox.MouseWheel += RichTextBox_MouseWheel;
}
// Methods
public void SuspendPainting()
{
if (m_painting)
{
m_suspendIndex = m_richTextBox.SelectionStart;
m_suspendLength = m_richTextBox.SelectionLength;
SendMessage(m_richTextBox.Handle, EM_GETSCROLLPOS, 0, ref m_scrollPoint);
SendMessage(m_richTextBox.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
m_eventMask = SendMessage(m_richTextBox.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
m_painting = false;
}
}
public void ResumePainting()
{
if (!m_painting)
{
m_richTextBox.Select(m_suspendIndex, m_suspendLength);
SendMessage(m_richTextBox.Handle, EM_SETSCROLLPOS, 0, ref m_scrollPoint);
SendMessage(m_richTextBox.Handle, EM_SETEVENTMASK, 0, m_eventMask);
SendMessage(m_richTextBox.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
m_painting = true;
m_richTextBox.Invalidate();
}
}
public void Append(StringBuilder p_message)
{
var WatchDogTimer = Stopwatch.StartNew();
var MinimumRefreshRate = 2000;
m_richTextBox
.Invoke((Action)delegate
{
// Last resort cleanup
if (WatchDogTimer.ElapsedMilliseconds > MinimumRefreshRate)
{
// m_richTextBox.Clear(); // Real-world behaviour
// Sample App behaviour
Form1.CancelAllProducers();
}
// Stop Drawing to prevent flickering during append and
// allow Double-Click events to register properly
this.SuspendPainting();
m_richTextBox.SelectionStart = m_richTextBox.TextLength;
m_richTextBox.SelectedText = p_message.ToString();
// Cap out Max Lines and cut back down to improve responsiveness
if (m_richTextBox.Lines.Length > 4000)
{
var NewSet = new string[1000];
Array.Copy(m_richTextBox.Lines, 1000, NewSet, 0, 1000);
m_richTextBox.Lines = NewSet;
m_richTextBox.SelectionStart = m_richTextBox.TextLength;
m_richTextBox.SelectedText = "\r\n";
}
this.ResumePainting();
// AutoScroll down to display newest text
if (m_attachToBottom)
{
m_richTextBox.SelectionStart = m_richTextBox.Text.Length;
m_richTextBox.ScrollToCaret();
}
});
}
// Event Handler
void RichTextBox_DoubleClick(object sender, EventArgs e)
{
// Toggle
m_attachToBottom = !m_attachToBottom;
// Scroll to Bottom
if (m_attachToBottom)
{
m_richTextBox.SelectionStart = m_richTextBox.Text.Length;
m_richTextBox.ScrollToCaret();
}
}
void RichTextBox_MouseWheel(object sender, MouseEventArgs e)
{
m_attachToBottom = false;
}
}
public class BufferedConsumer<TBuffer, TItem> : IDisposable
where TBuffer : new()
{
// Fields
private bool m_disposed = false;
private Task m_consumer;
private string m_name;
private CancellationTokenSource m_tokenSource;
private AutoResetEvent m_flushSignal;
private BlockingCollection<TItem> m_queue;
// Constructor
public BufferedConsumer(string p_name, Action<TBuffer, TItem> p_appendBuffer, Action<TBuffer> p_postBuffer)
{
m_name = p_name;
m_queue = new BlockingCollection<TItem>();
m_tokenSource = new CancellationTokenSource();
var m_token = m_tokenSource.Token;
m_flushSignal = new AutoResetEvent(false);
m_token
.Register(() => { m_flushSignal.Set(); });
// Begin Consumer Task
m_consumer = Task.Factory.StartNew(() =>
{
//Handler
// .LogExceptions(ErrorResponse.SupressRethrow, () =>
// {
// Continuously consumes entries added to the collection, blocking-wait if empty until cancelled
while (!m_token.IsCancellationRequested)
{
// Block
m_flushSignal.WaitOne();
if (m_token.IsCancellationRequested && m_queue.Count == 0)
break;
// Consume all queued items
TBuffer PostBuffer = new TBuffer();
Console.WriteLine("Queue Count = " + m_queue.Count + ", Buffering...");
for (int i = 0; i < m_queue.Count; i++)
{
TItem Item;
m_queue.TryTake(out Item);
p_appendBuffer(PostBuffer, Item);
}
// Post Buffered Items
p_postBuffer(PostBuffer);
// Signal another Buffer loop if more items were Queued during post sequence
var QueueSize = m_queue.Count;
if (QueueSize > 0)
{
Console.WriteLine("Queue Count = " + QueueSize + ", Sleeping...");
m_flushSignal.Set();
if (QueueSize > 10 && QueueSize < 100)
Sleep.For(1000, m_token); //Allow Queue to build, reducing posting overhead if requests are very frequent
}
}
//});
}, m_token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool p_disposing)
{
if (!m_disposed)
{
m_disposed = true;
if (p_disposing)
{
// Release of Managed Resources
m_tokenSource.Cancel();
m_flushSignal.Set();
m_consumer.Wait();
}
// Release of Unmanaged Resources
}
}
// Methods
public void AppendCollection(TItem p_item)
{
m_queue.Add(p_item);
m_flushSignal.Set();
}
}
public static partial class Sleep
{
public static bool For(int p_milliseconds, CancellationToken p_cancelToken = default(CancellationToken))
{
//p_milliseconds
// .MustBeEqualOrAbove(0, "p_milliseconds");
// Exit immediate if cancelled
if (p_cancelToken != default(CancellationToken))
if (p_cancelToken.IsCancellationRequested)
return true;
var SleepTimer =
new AutoResetEvent(false);
// Cancellation Callback Action
if (p_cancelToken != default(CancellationToken))
p_cancelToken
.Register(() => SleepTimer.Set());
// Block on SleepTimer
var Canceled = SleepTimer.WaitOne(p_milliseconds);
return Canceled;
}
}
}
Posting answer as per the OP's request:
You can integrate my example of a Virtualized, High-Performance, Rich, highly customizable WPF log Viewer in your existing winforms application by using the ElementHost
Full source code in the link above

J2me+images in form

I want to make the clock application where user enters the number in the textbox and click ok then user get the number at every 1 sec.
Example if user enter 5 then the timer start the display screen shows the number 1,2,3,4,5,0,1,2,3,4,5,0,1,2,3,...so on.
Now i had taken form and text field for user to enter the number,then a timer which will change the number at every second.and 10 images of number (0-9).As i want to dispaly the number in very large size.Now i had implement this logic in below way:-
public class Clock extends MIDlet implements CommandListener {
public Command GO, Exit;
TextField TxtData;
protected Display display;
int number, counter;
Form form;
private Timer timer;
private TestTimerTask task;
boolean increment, time;
private StringItem s1 = new StringItem("", "");
Image image0;
Image image1;
Image image2;
Image image3;
Image image4;
Image image5;
Image image6;
Image image7;
Image image8;
Image image9;
Image[] secondAnimation;
protected void startApp() {
display = Display.getDisplay(this);
increment = true;
time = false;
form = new Form("Clock");
TxtData = new TextField("Number:-", "", 5, TextField.NUMERIC);
try {
image0 = Image.createImage("/images/0.png");
image1 = Image.createImage("/images/1.png");
image2 = Image.createImage("/images/2.png");
image3 = Image.createImage("/images/3.png");
image4 = Image.createImage("/images/4.png");
image5 = Image.createImage("/images/5.png");
image6 = Image.createImage("/images/6.png");
image7 = Image.createImage("/images/7.png");
image8 = Image.createImage("/images/8.png");
image9 = Image.createImage("/images/9.png");
secondAnimation = new Image[]{image0,image1,image2, image3, image4, image5, image6, image7, image8, image9};
} catch (IOException ex) {
System.out.println("exception");
}
GO = new Command("Go", Command.OK, 1);
Exit = new Command("Exit", Command.EXIT, 2);
form.append(TxtData);
form.append(s1);
form.addCommand(GO);
form.addCommand(Exit);
form.setCommandListener(this);
display.setCurrent(form);
}
protected void pauseApp() {
}
protected void destroyApp(boolean unconditional) {
timer.cancel();
notifyDestroyed();
}
public void commandAction(Command cmnd, Displayable dsplbl) {
String label = cmnd.getLabel();
if (label.equals("Go")) {
try {
System.out.println("txt==" + (TxtData.getString()));
if (!TxtData.getString().equalsIgnoreCase("")) {
counter = Integer.parseInt(TxtData.getString());
if (time) {
timer.cancel();
task.cancel();
}
number = 1;
timer = new Timer();
task = new TestTimerTask();
timer.schedule(task, 1000, 1000);
}
} catch (NumberFormatException ex) {
System.out.println("exception");
}
} else if (label.equals("Exit")) {
destroyApp(true);
}
}
private class TestTimerTask extends TimerTask {
public final void run() {
time = true;
s1.setText(""+ number);
if (counter < 10) {
form.append(secondAnimation[0]);
form.append(secondAnimation[0]);
form.append(secondAnimation[number]);
} else if (counter < 100) {
form.append(secondAnimation[0]);
form.append(secondAnimation[(number % 100) / 10]);
form.append(secondAnimation[(number % 10)]);
} else if (counter < 1000) {
form.append(secondAnimation[(number % 10)]);
form.append(secondAnimation[(number % 100) / 10]);
form.append(secondAnimation[(number / 100)]);
}
number++;
if (number == counter + 1) {
number = 0;
}
}
} }
But as the form goes on appending the image as timer moves it is not showing the desired output!
I had tried to do it through LWUIT but as i had user 10 .png files and adding LWUIT.jar file make the size of .jar file 557kb which is very heavy.
So i want to do it through normal forms only.
I cant use canvas as the keypad can vary like (touch,qwerty etc).So i need to do normal form or LWUIT only.Can anyone please help me for this.
I noticed you only append items but never remove - is that intended?
Also, did you try two different forms to animate instead of one? For simple test, say, fill them in parallel, just call setCurrent for one that is not displayed in the moment of update
//...
private void appendTwice(Image image) {
form1.append(image);
form2.append(image);
}
//...
public final void run() {
time = true;
s1.setText(""+ number);
if (counter < 10) {
appendTwice(secondAnimation[0]);
//...
}
display.setCurrent(number & 1 == 0 ? form1 : form2);
number++;
//...
}
//...

Resources