Unity (Custom Editor) Save Data When Exit Unity
I created a simple Custom Editor that shows how much time I spent on Unity. When the button on it is pressed, it records the start time in a Scriptable Object (It's dirty). When the button is pressed again, it records the end time. If the window is closed before the button is pressed, I use the OnDestroy() method to complete the recording. It works (I also use "ExecuteInEditMode"). Here is the problem: If I close Unity without pressing the button, the OnDestroy() method does not work this time. Is there any way to fix this?
1 answer
-
answered 2020-11-03 19:02
derHugo
You could add a callback to
EditorApplication.quitting
Unity raises this event when the editor application is quitting.
Add an event handler to this event to receive a notification that the application is quitting.
Note that this will not fire if the Editor is forced to quit or if there is a crash. This event is raised when the quitting process cannot be cancelled.
Note btw that you could do it completely automated without a button or a ScriptableObject:
using UnityEngine; using UnityEditor; using System.Diagnostics; public static class LogEditorTime { private static bool isLogging; private static readonly StopWatch sw = new StopWatch (); static void OnQuit() { sw.Stop(); var elapsedTime = sw.Elapsed; Debug.Log($"Quitting the Editor after {elapsedTime.Hours}h {elapsedTime.Minutes}m {elapsedTime.Seconds}.{elapsedTime.Milliseconds / 10}s"); } [InitializeOnLoadMethod] static void OnLoad() { if(isLogging) return; sw.Restart(); EditorApplication.quitting -= OnQuit; EditorApplication.quitting += OnQuit; isLogging = true; } }
See also questions close to this topic
-
golf ball hitting golf club physics
I am making a simple 3D third person golfing game in unity and I have a problem. my character has a script that allows him to play an animation of him swinging a club when I press the left mouse key, and I have a golf ball. (the golf ball is a sphere with a sphere collider and a rigidbody on it) my problem is, when I swing the golf club at the golf ball, it doesnt get launched forward like I want it to (like regular golf balls being hit with golf clubs do) it just rolls away. is there a way to make it so when I play the golf club animation, and the golf ball collides with the club, it will be launched up and forward like an arch? please help! (remember this is a 3D third person game!)
-
Unity3D: How to properly stop a Wheel Collider?
Suppose I have a car with two throttle wheels. The code for acceleration is basically this (simplified).
float maxTorque = 1000f; float gas = Input.GetAxis("Acceleration"); //this actually goes on Update() //this goes on FixedUpdate() foreach(WheelCollider wheel in throttleWheels) wheel.motorTorque = gas* maxTorque;
With this I expect to go forward when
gas
(the acceleration input) is greater than 0 and reverse otherwise.When it's value is 0 (no input), it should decelerate, cause
motorTorque
multiplies togas
(0). But this is not what's happening in this last part, actually, the car keeps increasing speed!I tried add a brake torque:
if(gas == 0) foreach(WheelCollider wheel in throttleWheels) wheel.brakeTorque = 500f;
But still no luck. The only way I found to stop the object is decreasing it's
rigidbody.velocity
when the acceleration input is inversely the current rpm. Like this:float currentRpm = throttleWheels[0].rpm; //Check if player is braking while forward or accelerating while reverse if(currentRpm > 0 && acceleration < 0 || currentRpm < 0 && acceleration > 0) rigidbody.velocity *= 0.7f;
But I know this isn't the correct to brake the car, cause if the rigidbody hits something, the car stops but torque continues going up and the wheel keeps spinning.
Also tried to mix up to, the last method with brake torque, but these brakes seems useless, I really don't know how to set them up correctly.
So I'm having really bad times with Wheel Colliders, and I suppose it should not happen with something this basic, like accelerate and brake. Any help?
-
Unity track object position
I'm trying to make a cube to stay on a moving object. I want to track the object's position. How can I do that? I have searched for answers on unity's website. I did not find any answer/ that worked for me.
Is it okay to move my cube to the object's position with a transform? (
transform.position = new Vector3(object.position.x, transform.position.y, object.position.z)
) ? Or there is a better way to move my cube to the position of the object? -
No detect new lines inside a file for contruct a csv file in bash
I am making a csv constructor but I have some problems.
I have files like this
characteristics.tmp
1083108343 1083108360 1083108378 1083108386
I have to build to this format:
Name, "1083108343 ,1083108360 , 1083108378,1083108386"
;
I tried thiscat characteristics.tmp| paste -s -d, -
I also tried
tr '\n' ','
But the output is like if there is only one element. I check the file with xxd and the character
0d0a
is as it should be in the end of every line? So why is not detecting the others?,1083108386
-
Improving SED Efficiency
I'm currently working on a shell script that uses SED to replace many different strings in many different files. I currently have dozens of statements as such:
find . -type f -name "*.c*" -print0 | xargs -0 sed -E -i 's/#pragma data = (\w+)/DATA(\1)/g' find . -type f -name "*.c*" -print0 | xargs -0 sed -E -i 's/#pragma data1 = (\w+)/DATA1(\1)/g' find . -type f -name "*.c*" -print0 | xargs -0 sed -E -i 's/#pragma data2 = (\w+)/DATA2(\1)/g'
As far as I understand, these statements will cause the same file to be iterated over multiple times (once for every statement) as opposed to only iterating through each file once. Is there anyway to iterate through each file only once (to improve efficiency) and still make all the changes? Also, are there any other performance enhancements I could do to make the script run faster? It currently takes several minutes to run. Thanks! :)
-
Why JSON data shows "undefined" in JavaScript?
Hey am trying to show some quotes using my JSON file. But it shows undefined. Can anyone check the below html and JavaScript.
My Javascript
const resultEl = document.querySelector('.allquotes'); const pageSize = document.querySelector('select[name="page-size"]'); const pageCurr = document.querySelector('input[name="page-curr"]') const resultCount = document.querySelector('.result-count') const pageNoCurr = document.querySelector('.page-no-curr'); const pageNoCount = document.querySelector('.page-no-count') const btnFirst = document.querySelector('.page-btn-first'); const btnPrev = document.querySelector('.page-btn-prev'); const btnNext = document.querySelector('.page-btn-next'); const btnLast = document.querySelector('.page-btn-last'); let results = []; const getResultCount = () => results.length; const getPageSize = () => +pageSize.value; const getCurrPage = () => +pageCurr.value; const getPageCount = () => Math.ceil(getResultCount() / getPageSize()); const pageResponse = (records, pageSize, page) => (start => records.slice(start, Math.min(records.length, start + pageSize))) (pageSize * (page - 1)); const main = async() => { btnFirst.addEventListener('click', navFirst); btnPrev.addEventListener('click', navPrev); btnNext.addEventListener('click', navNext); btnLast.addEventListener('click', navLast); pageSize.addEventListener('change', changeCount); results = await retrieveAllQuotes(); updatePager(results); redraw(); }; const redraw = () => { resultEl.innerHTML = ''; const paged = pageResponse(results, getPageSize(), getCurrPage()); const contents = document.createElement('div'); contents.innerHTML = paged.map(record => `<div class='latestatus'><p class='copytxt'>${record.quotes}</p><div> <button class="copystatus btn">Copy</button></div></div>`).join(''); resultEl.append(contents); }; const navFirst = (e) => { pageNoCurr.textContent = 1; pageCurr.value = 1; redraw(); } const navPrev = (e) => { const pages = getPageCount(); const curr = getCurrPage(); const prevPage = curr > 1 ? curr - 1 : curr; pageCurr.value = prevPage; pageNoCurr.textContent = prevPage; redraw(); } const navNext = (e) => { const pages = getPageCount(); const curr = getCurrPage(); const nextPage = curr < pages ? curr + 1 : curr; pageCurr.value = nextPage; pageNoCurr.textContent = nextPage; redraw(); } const navLast = (e) => { pageNoCurr.textContent = getPageCount(); pageCurr.value = getPageCount(); redraw(); } const changeCount = () => { updatePager(); redraw(); }; const updatePager = () => { const count = getPageCount(); const curr = getCurrPage(); pageCurr.value = curr > count ? 1 : curr; pageNoCurr.textContent = curr > count ? 1 : curr; pageNoCount.textContent = count; resultCount.textContent = getResultCount(); }; const retrieveAllQuotes = async function() { // here we are making a network call to your api const response = await fetch('/stat.json'); // then converting it to json instead of a readable stream const data = await response.json(); // finally go over the array and return new object with renamed key const results = data.map(val => ({quotes: val.status})); return results; } main();
My Html
<div class="allquotes"></div> <div class="pagable-status"> <label>Page <span class="page-no-curr">1</span> of <span class="page-no-count">1</span></label> <div class="pagable-actions"> <button class="page-btn-first">≪</button> <button class="page-btn-prev"><</button> <input type="number" name="page-curr" min="1" value="1" /> <button class="page-btn-next">></button> <button class="page-btn-last">≫</button> <select name="page-size"> <option>5</option> <option>10</option> <option>20</option> </select> </div> <label>(<span class="result-count"></span> items)</label> </div>
I said that I use a JSON file. You can notice it on the javascript. There is a file name called stat.json. Location in my javascript
const retrieveAllQuotes = async function() { // here we are making a network call to your api const response = await fetch('/stat.json');
The content of stat.json is
[ { "quotes":"Do what is right not what is easy." }, { "quotes":"Hey there, Whatsapp is using me." }, { "quotes":"Too Busy" }, { "quotes":"Only you can give me that feeling." }, { "quotes":"I had a horribly busy day in converting oxygen into carbon dioxide" }, { "quotes":"Be yourself; everyone else is already taken" }, { "quotes":"Your attitude may hurt me, But main can Kill You!!" }, { "quotes":"Love is Blind, be careful." }, { "quotes":"'SUCCESS' is depend on U." }, { "quotes":"If you love someone set it free." }, { "quotes":"Love is sweet, When it's new. But it is sweeter when it's true." }, { "quotes":"Where ther is love, there is life." }, { "quotes":"Not always 'Available' try your luck.." }, { "quotes":"I am not changed it's just I grew up and you should try too." }, { "quotes":"The biggest slap to your enimies is your success." }, { "quotes":"Born to express, not to impress." }, { "quotes":"When nothing goes right! go left." }, { "quotes":"I allow myself to be badass confident in all that I do." }, { "quotes":"Sometimes you succeed and other time you learn." }, { "quotes":"A true friend sees the first tear, catches the second and stops the third." }, { "quotes":"We carry our childhood with us!!" }, { "quotes":"Childhood is the most beautiful of all lige's season!!" } ]
And these quotes want to displayed in a
<p class="copytxt">
element created by Javascript. Location in my javascriptcontents.innerHTML = paged.map(record => `<div class='latestatus'><p class='copytxt'>${record.quotes}</p><div> <button class="copystatus btn">Copy</button></div></div>`).join(''); resultEl.append(contents); };
If I run this code it shows all the quotes undefined. PLEASE if anyone have time run this above code and test it twice.
-
Groovy 3 support in Eclipse Groovy Editor
I have installed the latest release of the Groovy Development Tools in my Eclipse 2020-12. As I try to use Groovy 3.0 features. for example,
do { } while()
, it is flagged as anUnexpected Token
syntax error. The documentation says that this version does support Groovy 3. Is there perhaps a setting to set up the Groovy compliance level for the editor? My currently selected Groovy Compiler is v3.Thank you! Oleg
-
How to set Visual Studio 2019 to auto complete {} but not (), "", and []?
How can I set Visual Studio 2019 to auto complete
{}
but not()
,“”
, and[]
?This is a screenshot of a setting which will disable all brace completion, but isn't what I want:
This can be done in Dev-C.
-
How can open editor-x from phone
How can open editor-x from phone he tell me go to big screen but i don't want to open laptop what i do is there magic trick to do that? And
thanks
String s="thanks";
Println(s);
-
How to save value of counter when onDestroy callback is called?
I'm working on a small and simple android app where I increase or decrease a counter on button click. When the app goes
onStop
, I'm saving the current counter value in the text file and when I come back to the app, I'm reading the written value and showing it on screen in theTextView
. The problem is that when I come back to the app and receive the value and try to increment it, it starts to count from 0. However, the problem happens if I leave the app andonDestroy
callback is called, but if I rotate the phone and the sameonDestroy
are called the counter works as it is expected to work. My question is how to continue increasing the counter from the latest saved value?Here is my code:
MainActivity
public class MainActivity extends AppCompatActivity { private static final String FILE_NAME = "test.txt"; TextView textView; private int count; private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.text1); readTextFile(); if (savedInstanceState != null){ count = savedInstanceState.getInt("count"); textView.setText(String.valueOf(count)); } } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("count", count); } public void decrement(View view) { count --; textView.setText(String.valueOf(count)); } public void increment(View view) { count++; textView.setText(String.valueOf(count)); } @Override protected void onStop() { super.onStop(); String text = String.valueOf(count); FileOutputStream fos = null; try { fos = openFileOutput(FILE_NAME, MODE_PRIVATE); try { fos.write(text.getBytes()); Toast.makeText(this, "Saved to " + getFilesDir() + FILE_NAME, Toast.LENGTH_LONG).show(); } catch (IOException e) { e.printStackTrace(); } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } count = Integer.parseInt(text); Log.i(TAG,"onStop"); } @Override protected void onPause() { super.onPause(); Log.i(TAG,"onPause"); } @Override protected void onDestroy() { super.onDestroy(); Log.i(TAG,"onDestroy"); } public void readTextFile(){ FileInputStream fis = null; try { fis = openFileInput(FILE_NAME); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); StringBuilder sb = new StringBuilder(); String text; while ((text = br.readLine()) != null){ sb.append(text); } textView.setText(sb.toString()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if (fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
layout:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:screenOrientation="sensorLandscape" tools:context=".MainActivity"> <TextView android:id="@+id/text1" android:layout_width="167dp" android:layout_height="45dp" android:gravity="center" android:text="0" android:textColor="#0C0C0C" android:textSize="36sp" android:textStyle="bold" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.603" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="decrement" android:text="-" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/text1" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="increment" android:text="+" app:layout_constraintBottom_toTopOf="@+id/text1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
-
Reinitialising Fragment when closing activity
I have an Activity with a fragment, this fragment has a map in it and some markers. I run a service in the background that changes the position of the markers.
Each Marker has a boolean "isDrawn" and whenever the boolean is set to false the map is updated by adding drawing the marker ( I use LiveData to observe the markers) Whenever I close the Activity that contains the fragment I call onDestroyView, which sets isDrawn of every marker to false. That way when I open the Activity again, the markers get drawn one more time. All of this works fine.
The problem is this: in the Fragment I can tap on the markers, which opens a view that has a button which opens another Activity, when I close this Activity (With BackButton) and if a marker changed its position (through the service) when I was in said Activity, I find that there are two of the same marker on the map.
Any idea what I could do? Should I remove the fragment and create it again when I close the activity? is that possible? if so how should I proceed?
-
unable to destroy activity Android
here is my java code
@Override protected void onDestroy() { int i; super.onDestroy(); if (bitmapList != null) { for (i = INDEX_COLLAGE; i < bitmapList.length; i += INDEX_COLLAGE_BACKGROUND) { if (bitmapList[i] != null) { bitmapList[i].recycle(); } } } if (collageView != null) { if (collageView.shapeLayoutList != null) { for (i = INDEX_COLLAGE; i < collageView.shapeLayoutList.size(); i += INDEX_COLLAGE_BACKGROUND) { for (int j = INDEX_COLLAGE; j < collageView.shapeLayoutList.get(i).shapeArr.length; j += INDEX_COLLAGE_BACKGROUND) { if (collageView.shapeLayoutList.get(i).shapeArr[j] != null) { collageView.shapeLayoutList.get(i).shapeArr[j].freeBitmaps(); } } } } if (collageView.maskBitmapArray != null) { for (i = INDEX_COLLAGE; i < collageView.maskBitmapArray.length; i += INDEX_COLLAGE_BACKGROUND) { if (collageView.maskBitmapArray[i] != null) { if (!collageView.maskBitmapArray[i].isRecycled()) { collageView.maskBitmapArray[i].recycle(); } collageView.maskBitmapArray[i] = null; } } } } if (adWhirlLayout != null) { adWhirlLayout.removeAllViews(); adWhirlLayout.destroy(); } } private void backButtonAlertBuilder() { AlertDialog.Builder builder = new AlertDialog.Builder(CreateCollageActivity.this); builder.setMessage("Would you like to save image ?").setCancelable(true).setPositiveButton("Yes", new OnClickListener() { public void onClick(DialogInterface dialog, int id) { if (analytics != null) analytics.logEvent(Analytics.Param.IMAGE_SAVE, ""); new SaveImageTask().execute(); } }).setNeutralButton("No", new OnClickListener() { public void onClick(DialogInterface dialog, int which) { finish(); } });
The app crashes when i click back button..when i debugged the code it showed java.lang.RuntimeException: Unable to destroy activity and HVDactivities.CreateCollageActivity.onDestroy(CreateCollageActivity.java:740)
-
Scriptable Objects Reorder Parent/Child when renamed
How I got here: I have been working on a tool that uses scriptable objects which have child scriptable objects. The system works really well, however when I rename the SO the parent/child relationship breaks in Unity.
I have managed to get this down to a very simple case study now.
Using the code below, you create a Container SO asset. Then using the MyMenu you create the Data which is a child to the Container. All this works as expected and the relationship in the Project is:
Container -> Data
However, once you rename the Container SO to something else, Unity reorders the Parent Child so that the Data is now the Parent and Container is the Child:
Data -> Container
Below is the code that should duplicate this issues (Unity 2020.1.17f1 - Also had same results on 2020.1.8)
Container.cs
using UnityEngine; [CreateAssetMenu] public class Container : ScriptableObject { }
Data.cs
using UnityEngine; public class Data : ScriptableObject { }
CustomEditor.cs
using UnityEditor; using UnityEngine; public class CustomEditor : Editor { [MenuItem("MyMenu/Create Data for Container")] static void CreateDataForContainer() { Container container = Selection.activeObject as Container; if (container != null) { Data data = ScriptableObject.CreateInstance(typeof(Data)) as Data; data.name = data.GetType().Name; if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(container))) { AssetDatabase.AddObjectToAsset(data, container); } AssetDatabase.SaveAssets(); } } }
NOTES
- At first I thought this was an issue with namespaces but not the case
- Thought this could be an assembly issue but not the case now with basic classes
- Close/Open project results are same
- ASSET file is identical after rename
If you experienced this I'd love to know what I'm doing wrong here, my tool is almost done and this is stopping me from pushing it out. Thanks!
-
ScriptableObject, added a field, Assets not updating, how to?
Newly added field in ScriptableObject class not being put into assets of its type. Is this something todo with how Scriptable Objects work? Is there a way to update a scriptable object asset without losing existing info in already populated fields?
My ScriptableObject class has a bunch of strings for animation:
public class SO_AnimStringNames : ScriptableObject { public string FirstAnim; public string SecondAnim; public string ThirdAnim; }
I've created a ScriptableObject asset via this class, and added names to the strings, and used it as data within another object type, that uses those strings.
Just now, added a fourth string to the ScriptableObject class, so now it looks like this:
public class SO_AnimStringNames : ScriptableObject { public string FirstAnim; public string SecondAnim; public string ThirdAnim; public string FourthAnim; // newly added field }
but it's not seen/updated within the ScriptableObject asset, and there's no obvious way to update this asset.
Is this a limitation of ScriptableObjects, or is something else wrong?
-
How do I find all Scriptable Objects in a folder and then load a variable from one?
I have a folder in my unity project under "Resources/ScriptableObjects/Skins". I need to get all the objects in the folder, generate a random number for the index, then assign the sprite to an existing game object that the script is attached to. The current error I'm getting is "NullReferenceException: Object reference not set to an instance of an object" on line 151 but I'm creating an instance of the object on line 149. What gives? Here is my function to assign the sprite from a random scriptableobject in the folder to the game object the script is tied to:
void AssignRandomSkin(){ // Load all skins into memory Object[] skinObjects = Resources.LoadAll("ScriptableObjects/Skins"); // Get length of list int amountOfSkins = skinObjects.Length; // Get random index of skin to assign int skinToAssignIndex = Random.Range(0, amountOfSkins); GameObject thisSkin = Instantiate(skinObjects[skinToAssignIndex]) as GameObject; // Assign it to game object gameObject.GetComponent<SpriteRenderer>().sprite = thisSkin.GetComponent<Sprite>(); }
Here is the Scriptable Object:
using UnityEngine; [CreateAssetMenu(fileName = "Skin", menuName = "ScriptableObjects/SkinObject", order = 1)] public class SkinObject : ScriptableObject { public string spriteName; // Name of sprite public Sprite sprite; public float xPos; public float yPos; public float zPos; public float xScale; public float yScale; public float zScale; public float fallSpeed; //AKA Weight public string tier; //Category that skin can be assigned in }