I'm having some problems with a physics project I'm trying to make.
Its basically a Free Fall simulator. I set the gravitational acceleration and the height and the program should simulate the fall.
The problem is that it's getting the time wrong, don't know why. I'm using the Stopwatch class for measuring the time.
Here's the code:
FrmMain.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
namespace Physics
{
public partial class FrmMain : Form
{
public Settings config = new Settings();
public float displacement = 0f;
public Stopwatch timer = new Stopwatch();
public float timeElapsed; //in Seconds
public Thread thread;
public FrmMain()
{
InitializeComponent();
//New thread (Drawing)
thread = new Thread(new ThreadStart(Drawing));
thread.IsBackground = true;
thread.Start();
this.KeyDown +=new KeyEventHandler(FrmMain_KeyDown);
}
private void Drawing()
{
try
{
while (true)
{
if (config.objectPos.Y < config.sizeBitmap.Height)
timer.Start();
else
timer.Stop();
Bitmap screen = new Bitmap(this.pboxScreen.Width,
this.pboxScreen.Height);
SendScreen(screen);
}
}
catch (ThreadAbortException tae)
{
Console.WriteLine(tae.Message);
}
}
private void SendScreen(Bitmap Screen)
{
if (pboxScreen.InvokeRequired)
{
pboxScreen.Invoke(new MethodInvoker(delegate()
{
this.SendScreen(Screen);
}));
}
else
{ //Converting Milliseconds to Seconds
timeElapsed = timer.ElapsedMilliseconds / 1000f;
//Check if object isn't in the ground
if (config.objectPos.Y < config.sizeBitmap.Height)
{
displacement -= config.objectPos.Y;
config.objectPos.Y = config.objectPos.Y + 0.5f *
config.acceleration *
timeElapsed * timeElapsed;
displacement += config.objectPos.Y;
}
Graphics g = Graphics.FromImage(Screen);
g.Clear(Color.White);
//New rectangle
Rectangle rec = new Rectangle((int)config.objectPos.X,
(int)config.objectPos.Y, 5, 5);
g.FillRectangle((new Pen(Color.DarkRed)).Brush, rec);
g.DrawRectangle(new Pen(Color.Red, 2.0f), rec);
g.Dispose();
//Update txtbox (textbox)
txtboxX.Text = config.objectPos.X.ToString();
txtboxY.Text = config.objectPos.Y.ToString();
txtboxTempo.Text = timeElapsed.ToString();
txtboxD.Text = displacement.ToString();
pboxScreen.Image = Screen;
}
}
void FrmMain_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Space:
if (config.objectPos.Y >= config.sizeBitmap.Height)
{
config.objectPos.Y -= 100;
timer.Reset();
}
break;
}
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (thread.IsAlive) thread.Abort();
}
}
}
Settings.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace Physics
{
public class Settings
{
public Size sizeBitmap; //bitmap resolution
public PointF objectPos; //Initial Position
public float acceleration;
public Settings()
{
sizeBitmap.Width = 560;
sizeBitmap.Height = 420;
objectPos.X = 560f / 2f;
objectPos.Y = 420f / 2f;
acceleration = 9.8f;
}
}
}
There's another problem, depending on height the object doesn't fall without interruption, it stay in the same position for a few, but noticeable, milliseconds. I think this is a threading problem, 'cause it's been a while I don't use it, so I might be missing something.
I'm measuring the height in meters, so the objectPos.Y = 420f / 2f represent a height of 210 meters. For this height, the object should take around 6.5 seconds to hit the ground, in my program it's taking less than 1 second, so I assumed that there's a time measuring problem.
I'm calculating the height with the uniform gravitational field without air resistance expression :
h(t)=h0+0.5*g*t²
where: h(t) is the height with respect to time, h0 the initial altitude, g acceleration due to gravity and t the time elapsed
Any help is much appreciated.
Thanks.
I believe your issue may be in the line
config.objectPos.Y = config.objectPos.Y + 0.5f *
config.acceleration *
timeElapsed * timeElapsed;
The YPosition at time t = t-sub-k (config.objectPos.Y) is the height at time t-sub-k. if t is total elapsed time, then this is equal to the Initial Y-Position (The Height at Time t = 0) + 1/2 gt^2, NOT the last Y-Position.
What you are doing is taking the last Y-Position (the height as of the previous calculation, and adding the drop in altitude during the elapsed time from time t=0 to t= t-sub-k. So it's no wonder that it is falling too fast.
Add an Initial Y-Position read-only property to your settings construct, and DO not Update or modify it.
Modify your code to use it as follows:
config.objectPos.Y = config.InitialY + 0.5f *
config.acceleration *
timeElapsed * timeElapsed;
I think your formula is wrong.
h0 should be the initial velocity (which in your case is 0 m/s) and it should be multiplied with time.
This is the correct one from
So having said that, Vi*t is always 0 since your initial velocity is 0. What's left is 0.5 * 9.8 * timeElapsed * timeElapsed.
config.objectPos.Y = 0 //initial velocity
+ 0.5f * config.acceleration * timeElapsed * timeElapsed;
Also objectPost.Y should always start from 0 meters since it's starting from the top and it's going to fall.
You can refer to this Console app code. Run it and see the time elapsed compared and compare it to the results in
It should be 1.32 seconds.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Physics_16783733
{
class Program
{
static void Main(string[] args)
{
MyClass c = new MyClass();
}
}
public class MyClass
{
public Settings config = new Settings();
public Stopwatch timer = new Stopwatch();
public float timeElapsed; //in Seconds
public Thread thread;
static ManualResetEvent control;
public MyClass()
{
control = new ManualResetEvent(false);
//New thread (Drawing)
thread = new Thread(new ThreadStart(Drawing));
thread.IsBackground = true;
thread.Start();
control.WaitOne();
}
public void Drawing()
{
try
{
while (true)
{
if (config.objectPos.Y < config.sizeBitmap.Height)
{
timer.Start();
}
else
{
timer.Stop();
control.Set();
}
//Bitmap screen = new Bitmap(this.pboxScreen.Width, this.pboxScreen.Height);
SendScreen();
}
}
catch (ThreadAbortException tae)
{
Console.WriteLine(tae.Message);
}
}
private void SendScreen()
{
timeElapsed = timer.ElapsedMilliseconds / 1000f; //Converting Milliseconds to Seconds
if (config.objectPos.Y < config.sizeBitmap.Height) //Check if object isn't in the ground
{
//formula used is in http://www.physicsclassroom.com/Class/1DKin/U1L6c.cfm
config.objectPos.Y = 0 //initial velocity
+ 0.5f * config.acceleration * timeElapsed * timeElapsed;
}
//Update txtbox (textbox)
Console.WriteLine("Object position Y: " + config.objectPos.Y.ToString());
Console.WriteLine("Time elapsed : " + timeElapsed.ToString()); //using the data from http://www.physicsclassroom.com/Class/1DKin/U1L6c.cfm, time elapsed should be 1.32 seconds
}
}
public class Settings
{
//I used data from http://www.physicsclassroom.com/Class/1DKin/U1L6c.cfm
//where the given is:
//vi = 0.0 m/s
//d = -8.52 m
//a = - 9.8 m/s2
public Size sizeBitmap; //height of the place where the object will start free falling
public PointF objectPos; //Initial Position
public float acceleration;
public Settings()
{
sizeBitmap.Height = 8.52;
objectPos.Y = 0;
acceleration = 9.8f;
}
}
public struct PointF
{
public float Y { get; set; }
}
public struct Size
{
public double Height { get; set; }
}
}
Related
I have a code that once every 24 hours (86400000 milliseconds) if the player's score is over 7000 points, then it divides all points above 7000 by 2, but the problem is that when I get 7000+ points, the game freezes and no longer works never (everything worked well in unity). What could be the reason?
using System;
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using Random=UnityEngine.Random;
public class LeagueClock : MonoBehaviour
{
private ulong TimeLastReset;
[SerializeField] private float msToWait = 86400000f;
[SerializeField] private int scoreThreshold = 7000;
private int scrMain;
private int MainCoins;
private int LegendaryTrophies;
private void Start(){
LegendaryTrophies = PlayerPrefs.GetInt("LegendaryTrophies");
scrMain = PlayerPrefs.GetInt("scrMain");
TimeLastReset = ulong.Parse(PlayerPrefs.GetString("LastReset", "0"));
MainCoins = PlayerPrefs.GetInt("MainCoins");
Debug.Log(DateTime.Now);
}
private void Update(){
if (scrMain > 7000) {
//apply the action for each interval that has passed.
//For example, if the interval is 24 hours, and 49 hours
//have passed, that's 2 intervals, so we reduce the score
//twice.
int intervalsPassed = GetIntervalsPassed();
if (intervalsPassed > 0){
for (int i = 0; i < intervalsPassed; i++) {
ReduceScore();
}
PlayerPrefs.SetInt("scrMain", scrMain);
TimeLastReset = (ulong)DateTime.Now.Ticks;
PlayerPrefs.SetString("LastReset", TimeLastReset.ToString());
}
}
}
private void ReduceScore() {
MainCoins += ((scrMain-scoreThreshold)/2)*100;
LegendaryTrophies += (scrMain-scoreThreshold)/2;
scrMain -= (scrMain-scoreThreshold)/2;
PlayerPrefs.SetInt("MainCoins", MainCoins);
PlayerPrefs.SetInt("scrMain", scrMain);
PlayerPrefs.SetInt("LegendaryTrophies", LegendaryTrophies);
PlayerPrefs.Save();
}
//returns the number of full time intervals that have passed since
//we last reduced the score
private int GetIntervalsPassed(){
ulong diff = ((ulong)DateTime.Now.Ticks - TimeLastReset);
ulong ms = diff / TimeSpan.TicksPerMillisecond;
float secondsLeft = (float)(msToWait - ms) / 1000.0f;
int intervalsPassed = Mathf.FloorToInt(ms / msToWait);
Debug.Log($"SecondsLeft: {secondsLeft} | Intervals: {intervalsPassed} | Score: {scrMain}");
return intervalsPassed;
}
}
It might memory problem that affects your game you can look at these documents, and I hope this helps Unity Memory
My game implements a datetime system which contains 3 speeds:
Slow: 1 second = 10 in-game minutes,
Normal: 1 second = 2 in-game hours,
Fast: 1 second = 1 in-game day
Slow speed works fine, normal speed have a bit of delay, and fast speed is delay by a lot (like 2.5 sec for a day). Is it because the computer cannot handle too much operation? Is there any way to implement it? Thanks.
Below is my script, important part starts at the update function.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
//Manage datetime
public class DateManager : MonoBehaviour
{
//Singleton
public static DateManager dateManager;
//Get other objects
private GameStatManager gameStatManager;
//Local var
private Text displayTime;
private Text displayDate;
private Image timeScaleImage;
float counter;
//Get speed icon
public Sprite slow;
public Sprite medium;
public Sprite fast;
//Output var
public DateTime date;
private float timeScale;
void Start(){
//Singleton
dateManager = this;
//Get other objects
gameStatManager = GameObject.Find("GameStatManager").GetComponent<GameStatManager>();
displayDate = transform.Find("Date").gameObject.GetComponent<Text>();
displayTime = transform.Find("Time").gameObject.GetComponent<Text>();
timeScaleImage = transform.Find("TimeScale").gameObject.GetComponent<Image>();
//Set default
timeScale = 1f;
timeScaleImage.sprite = slow;
counter = 0f;
date = new DateTime(2010, 01, 01);
}
void Update()
{
counter += Time.deltaTime;
if (counter >= 1f / timeScale){
counter = 0f;
date = date.AddMinutes(10);
displayDate.text = date.ToString("dd MMM, yyyy");
displayTime.text = date.ToString("HH:mmtt");
//To GSM
gameStatManager.SetDate(date);
}
}
//Button function
public void ChangeTimeScale(){
// 1f = 10mins/sec || 12f = 2hrs/sec || 144f = 1day/sec
if (timeScale == 1f){
timeScale = 12f;
timeScaleImage.sprite = medium;
}
else if (timeScale == 12f){
timeScale = 144f;
timeScaleImage.sprite = fast;
}
else {
timeScale = 1f;
timeScaleImage.sprite = slow;
}
//To GSM
gameStatManager.SetTimeScale(timeScale);
}
}
The first script is for the F pressing and calling the ScaleChange:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DroidMove : MonoBehaviour
{
public GameObject droid;
public ChangeScale changeScale;
private bool toDisplay = false;
private void Start()
{
droid.transform.localScale = new Vector3(0, 0, 0);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
toDisplay = !toDisplay;
changeScale.Scale(toDisplay);
}
}
}
The second script make the scaling:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChangeScale : MonoBehaviour
{
public GameObject objectToScale;
private float _currentScale = InitScale;
private const float TargetScale = 0.7f;
private const float InitScale = 0f;
private const int FramesCount = 10;
private const float AnimationTimeSeconds = 0.1f;
private float _deltaTime = AnimationTimeSeconds / FramesCount;
private float _dx = (TargetScale - InitScale) / FramesCount;
private IEnumerator ScaleUp()
{
bool upscaling = true;
while (upscaling)
{
_currentScale += _dx;
if (_currentScale > TargetScale)
{
upscaling = false;
_currentScale = TargetScale;
}
objectToScale.transform.localScale = Vector3.one * _currentScale;
yield return new WaitForSeconds(_deltaTime);
}
}
private IEnumerator ScaleDown()
{
bool downscaling = true;
while (downscaling)
{
_currentScale -= _dx;
if (_currentScale < InitScale)
{
downscaling = false;
_currentScale = InitScale;
}
objectToScale.transform.localScale = Vector3.one * _currentScale;
yield return new WaitForSeconds(_deltaTime);
}
}
public void Scale(bool scaleUp)
{
if (scaleUp)
{
StartCoroutine(ScaleUp());
}
else
{
StartCoroutine(ScaleDown());
}
}
}
The problem is if I press on F and it start scaling up and not finished yet the scaling and I press F again instead start scaling down it's like pausing and if I keep pressing F it will keep scaling up each time a bit.
But what I want to do is when I press first time F start scaling up and if in the middle while scaling i'm pressing F again then from the current scaling point start scaling down and if I press F again then up and down like a smooth switch.
But now it's just pausing each time I press F many times.
It's like I need to wait now for the scaling to finish first before I can press F again if not it will just pause it and i want that each time I press F it will switch the scaling up/down.
You don't need multiple functions for scaling up and down. One should do it. Just make it take a scale amount as parameter then re-use that function. The scale Min and Max should be a Vector3 not float since that's how scale is represented and you want to cover all three axis (x,y,z).
How to fix the problem of scaling up taking so long to finish and not switching direction when key is pressed:
When you start a coroutine, get a reference to that running coroutine. Before you start the coroutine again, use the old reference to stop the old one. It's as simple as that. It's much better to re-write the whole code than to fix the existing one you have.
Below is a simplified version of your code. Make sure to set the minSize, maxSize and objectToScale variables from the Editor. See code comments if you have any question.
public GameObject objectToScale;
public Vector3 minSize;
public Vector3 maxSize;
private bool scaleUp = false;
private Coroutine scaleCoroutine;
// Use this for initialization
void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
//Flip the scale direction when F key is pressed
scaleUp = !scaleUp;
//Stop old coroutine
if (scaleCoroutine != null)
StopCoroutine(scaleCoroutine);
//Scale up
if (scaleUp)
{
//Start new coroutine and scale up within 5 seconds and return the coroutine reference
scaleCoroutine = StartCoroutine(scaleOverTime(objectToScale, maxSize, 5f));
}
//Scale Down
else
{
//Start new coroutine and scale down within 5 seconds and return the coroutine reference
scaleCoroutine = StartCoroutine(scaleOverTime(objectToScale, minSize, 5f));
}
}
}
IEnumerator scaleOverTime(GameObject targetObj, Vector3 toScale, float duration)
{
float counter = 0;
//Get the current scale of the object to be scaled
Vector3 startScaleSize = targetObj.transform.localScale;
while (counter < duration)
{
counter += Time.deltaTime;
targetObj.transform.localScale = Vector3.Lerp(startScaleSize, toScale, counter / duration);
yield return null;
}
}
I made a UI slider that moves according to a value in my script(playtime) and it runs smoothly so far. here's my slider code
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class testingtime : MonoBehaviour {
public int playtime =0;
public int seconds = 0;
private int minutes = 0;
private int hours = 0;
public int playtimemax = 0;
public Slider testslider;
public Texture playbutton;
public Texture Pausebutton;
Texture substitute;
bool ispaused = false;
// Use this for initialization
void Start () {
substitute = Pausebutton;
StartCoroutine ("playtimer");
}
private IEnumerator playtimer(){
while (true) {
yield return new WaitForSeconds(1);
playtime += 1;
seconds = (playtime % 60);
minutes = (playtime / 60) % 60;
hours = (playtime / 3600) % 24;
}
}
// Update is called once per frame
void Update () {
testslider.value = playtime;
if (ispaused) {
Time.timeScale = 0;
substitute = playbutton;
}
if (!ispaused) {
Time.timeScale = 1;
substitute = Pausebutton;
}
playtime = (int)testslider.value;
}
void OnGUI (){
if (GUI.Button (new Rect (685,710, 20, 20), substitute)) {
ispaused = !ispaused;
}
GUI.Label (new Rect (50, 50, 400, 50), "Playtime = " + hours.ToString () + " Hours " + minutes.ToString () + " Minutes " + seconds.ToString () + " Seconds");
}
}
So basically this makes my slider move according to playtime which increases by 1 every second like a timer.
However I tried to change the value of playtime through the slider (I have interactable enabled) with the code playtime = (int)testslider.value;.
I am aware that i cannot move the slider because it keeps updating testtimer.value = playtime.
Is there any function available, something like testtimer.onclicked, so that I can put it in a if function and disable the updating?
Slider has onValueChanged callback, which you can use for that purpose.
I got it guys! :D FINALLY
All you need is this code
public void adjusttimer(float newtime){
playtime = (int)newtime;
}
and assign the method like so
it should work just perfectly. :D
Much thanks to Max Yankov for the idea!
I have been trying to calculate the average of the raw data that I have been receiving from my camera. The reason is that at the moment I am just using the raw data and this causes quite abit of jitter. Therefore I am wanting the past 5 locations and then an average to be calculated as this would make the hand tracking experience much more smoother.
I have been trying to store the previous 5 locations but when I attempted this I only get the latest raw data which then get stored and overwrites it for the locations.
The OnNewFrame() method is where I get the raw data.
Help on how I would do this would great.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using SDKAttempt;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Drawing;
//The sdk module tracks geometric nodes for every frame
namespace PercUtils
{
class DepthPipeline : UtilMPipeline
{
double screenWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
double screenHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
PXCMGesture.GeoNode nData; //data the we will get from the camera
double xPos = 0;
double yPos = 0;
double zPos = 0;
double handOpeness = 0;
private bool finished;
PXCMGesture.GeoNode[] nodeData = new PXCMGesture.GeoNode[1]; //data from hand
public DepthPipeline()
: base()
{
finished = false;
EnableImage(PXCMImage.ColorFormat.COLOR_FORMAT_DEPTH);
EnableGesture(); //This function is called to enable finger tracking and gesture recognition
}
public override void OnGestureSetup(ref PXCMGesture.ProfileInfo pinfo) //the application can override this function to fine-tune any parameters during module initilisation
{
base.OnGestureSetup(ref pinfo);
}
public override void OnGesture(ref PXCMGesture.Gesture gesture) //the application needs to receive pose/gesture, the application can override the ongesture function
{
if (gesture.label == PXCMGesture.Gesture.Label.LABEL_POSE_THUMB_UP)
{
Console.WriteLine("Thumb Up"); //debug
}
base.OnGesture(ref gesture);
}
public override void OnAlert(ref PXCMGesture.Alert alert) //the application can override the OnAlert function to receive alert notifications
{
base.OnAlert(ref alert);
}
public override bool OnNewFrame()
{
double xRatio = screenWidth / 160; //corrects the ratio of the x axis of the camera to work with the width screen - scales the camera cood for the screen being used
double yRatio = screenHeight / 120; //corrects the ratio of the y axis of the camera to work with the height screen - scales the camera cood for the screen being used
PXCMGesture gesture = QueryGesture();
pxcmStatus sts = gesture.QueryNodeData(0, PXCMGesture.GeoNode.Label.LABEL_BODY_HAND_LEFT, out nData); //gets the status of the left hand, out nData because the value isn't set and the function must set it before returning it
if (sts >= pxcmStatus.PXCM_STATUS_NO_ERROR)
{
xPos = (screenWidth - (nData.positionImage.x * xRatio)) + (screenWidth / 2);
yPos = ((nData.positionImage.y * yRatio) - (screenHeight / 2));
zPos = nData.positionWorld.y * 100;
handOpeness = nData.openness;
TitleScreen.setX(xPos); //this corrects the inverted control of the dot
TitleScreen.setY(yPos);
TitleScreen.getHandOpenness(handOpeness);
PlayGame.setX(xPos); //this corrects the inverted control of the dot
PlayGame.setY(yPos);
PlayGame.getHandOpeness(handOpeness);
PlayGame.getZ(zPos); //gets the z coordnidate of the users hand.
PlayGameEasy.setX(xPos); //this corrects the inverted control of the dot
PlayGameEasy.setY(yPos);
PlayGameEasy.getHandOpeness(handOpeness);
PlayGameEasy.getZ(zPos); //gets the z coordnidate of the users hand.
CollisionClass.setX(xPos);
CollisionClass.setY(yPos);
CollisionClass.getZ(zPos);
Button.setX(xPos);
Button.setY(yPos);
Button.getHandOpeness(handOpeness);
ReplayScreen.getX(xPos);
ReplayScreen.getY(yPos);
HighScore.getX(xPos);
HighScore.getY(yPos);
DifficultyScreen.getX(xPos);
DifficultyScreen.getY(yPos);
}
return !finished;
}
public void Finish()
{
finished = true;
}
public bool IsFinished()
{
return finished;
}
public void StartWork()
{
finished = false;
this.LoopFrames();
}
}
}
Could you add an array of 5 double for each xpos, ypos, zpos and an indexAverage
Make all of them private class variables
In your new frame function use:
Xpos_array [indexAverage]= (screenWidth - (nData.positionImage.x * xRatio)) + (screenWidth / 2);
Xpos = Xpos_array.select(x => x / 5.0).sum();
[.....]
indexAverage = (indexAverage+1)%5;