Hello Guest

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - twicecircled

Pages: [1]
1
Hi Unikron,

Thanks so much for the quick reply, that's solved the issue nicely! Please except my apologies, I'm new to editor scripts and didn't realise about this SetDirty() method.

In case anybody else is reading this, the entire call required to solve the issue in this case is:

Code: [Select]
UnityEditor.EditorUtility.SetDirty(tk2dSpriteCollectionData);
This tells Unity that the prefab object has changed and it should be saved to the disc. Then when you reload the editor the changes are kept. The requirement is nothing to do with tk2d, you need to do this whenever you change any prefab in Unity.

In case anybody wants to use this code, I've reposted it below with this line added. Just attach the MaterialChanger.cs script to any gameobject and it will work as a little tool for switching the material attached to a sprite.

Code: [Select]
// MaterialChanger.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MaterialChanger : MonoBehaviour {

public tk2dSpriteCollectionData data;
public string spriteContains;
public Material newMaterial;

public void ChangeMaterial () {
// Add material if necessary
List<Material> materials = new List<Material>(data.materials);
if (!materials.Contains(newMaterial)){
materials.Add(newMaterial);
data.materials = materials.ToArray();
}

// Get material index
int mIndex = 0;
for(int i =0; i<materials.Count;i++){
if (materials[i]==newMaterial){
mIndex = i;
break;
}
}

foreach (tk2dSpriteDefinition def in data.spriteDefinitions) {
if (def.name.Contains(spriteContains)){
def.material = newMaterial;
def.materialId = mIndex;
}
}
UnityEditor.EditorUtility.SetDirty(data);
}
}

// MaterialChangerEditor.cs - needs to be placed in a folder called "Editor"
using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof(MaterialChanger))]
public class MaterialChangerEditor : Editor {

public override void OnInspectorGUI() {
MaterialChanger materialChanger = (MaterialChanger) target;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Sprite collection data");
materialChanger.data = (tk2dSpriteCollectionData) EditorGUILayout.ObjectField(materialChanger.data,typeof(tk2dSpriteCollectionData),false);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Identifier");
materialChanger.spriteContains = EditorGUILayout.TextField(materialChanger.spriteContains);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Material to change to");
materialChanger.newMaterial = (Material)EditorGUILayout.ObjectField(materialChanger.newMaterial,typeof(Material),false);
EditorGUILayout.EndHorizontal();

EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Do change", GUILayout.MaxWidth(120))){
materialChanger.ChangeMaterial();
}
EditorGUILayout.EndHorizontal();
}
}

2
Support / Permanently changing sprite material in collection data via code
« on: September 05, 2014, 02:07:09 pm »
Hi there, I'm looking for a way to permanently change the material associated with a given sprite in a given collection via code.

I understand that there is a GUI-based solution here http://www.unikronsoftware.com/2dtoolkit/docs/latest/tutorial/multiple_materials_in_a_sprite_collection.html however I have large sprite collections and this method requires multiple rebuilds of the entire atlas which is very time-consuming.

I've set up the following code, attached to a prefab and run from an editor script, which seems to do the job nicely and instantaneously without any rebuilding of the collection. The updates are effective in the player.

Code: [Select]
public class MaterialChanger : MonoBehaviour {

public tk2dSpriteCollectionData data;
public string spriteContains;
public Material newMaterial;

public void ChangeMaterial () {
// Add material if necessary
List<Material> materials = new List<Material>(data.materials);
if (!materials.Contains(newMaterial)){
materials.Add(newMaterial);
data.materials = materials.ToArray();
}

// Get material index
int mIndex = 0;
for(int i =0; i<materials.Count;i++){
if (materials[i]==newMaterial){
mIndex = i;
break;
}
}

foreach (tk2dSpriteDefinition def in data.spriteDefinitions) {
if (def.name.Contains(spriteContains)){
def.material = newMaterial;
def.materialId = mIndex;
}
}
}
}

// EDITOR:

[CustomEditor(typeof(MaterialChanger))]
public class MaterialChangerEditor : Editor {

public override void OnInspectorGUI() {
MaterialChanger materialChanger = (MaterialChanger) target;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Sprite collection data");
materialChanger.data = (tk2dSpriteCollectionData) EditorGUILayout.ObjectField(materialChanger.data,typeof(tk2dSpriteCollectionData),false);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Identifier");
materialChanger.spriteContains = EditorGUILayout.TextField(materialChanger.spriteContains);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Material to change to");
materialChanger.newMaterial = (Material)EditorGUILayout.ObjectField(materialChanger.newMaterial,typeof(Material),false);
EditorGUILayout.EndHorizontal();

EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Do change", GUILayout.MaxWidth(120))){
materialChanger.ChangeMaterial();
}
EditorGUILayout.EndHorizontal();
}
}

However, when I restart Unity my changes are overwritten and I'm forced to rerun this script again every time I restart. Is there a way to change the material references via code permanently, either on the tk2dspritecollectiondata or tk2dspritecollection prefab?

I'm quite happy going in and changing stuff directly on the tk2d prefabs and I understand the consequences of any changes I make. I'm not looking for a neat solution, just one that works. I am also absolutely fine with the changes being reversed every time I rebuild the sprite collection. My only requirement is that is does not reverse the changes on restart and, of course, that it does not required a Commit() of the collection.

PS Just noticed Private Support forum. Apologies if this should have gone in there. Feel free to move topic if so.

3
Support / Re: Frustration with tk2d
« on: May 22, 2014, 12:35:50 pm »
I thought I would share the code I'm using which lets you reverse an animation by simply switching a boolean on the tk2dSpriteAnimator itself. This involves changing the core code itself so obviously you will lose any modifications you make if you update/reinstall TK2D.

The nice thing about this code is you can reverse in the middle of the animation and it keeps its position.

Here's the changes I've made to the code:

TK2DROOT\tk2d\Code\Sprites\tk2dSpriteAnimator.cs
Line 47 - added a publically accessible bool which you can toggle to reverse the animation.
Code: [Select]
public bool reversed = false;
Line 546(ish) - Replace UpdateAnimation(float deltaTime) method - when 'reversed' is true, clipTime is decremented instead of incremented. I've also made a few changes to how it calculates the Frame number so that is supports negative clip time. Note: Reverse mode is only supported when on wrap mode "loop" as that is all I needed it for.
Code: [Select]
/// <summary>
/// Modified by Twice Circled to add reverse behaviour for looping anims - 22/05/14
/// Steps the animation based on the given deltaTime
/// Disable tk2dSpriteAnimator, and then call UpdateAnimation manually to feed your own time
/// eg. when you need an animation to play when the game is paused using Time.timeScale.
/// </summary>
public void UpdateAnimation (float deltaTime) {
// Only process when clip is playing
var localState = state | globalState;
if (localState != State.Playing) {
return;
}

// Current clip should not be null at this point
// Only supports reverse behaviour on 'loop'
if (reversed && currentClip.wrapMode == tk2dSpriteAnimationClip.WrapMode.Loop) {
clipTime -= deltaTime * clipFps;
} else {
clipTime += deltaTime * clipFps;
}
int _previousFrame = previousFrame;

switch (currentClip.wrapMode) {
case tk2dSpriteAnimationClip.WrapMode.Loop:
case tk2dSpriteAnimationClip.WrapMode.RandomLoop:
{
int currFrame = ((int)clipTime % currentClip.frames.Length + currentClip.frames.Length) % currentClip.frames.Length;
SetFrameInternal (currFrame);
// Only supports reverse behaviour on 'loop'
if (reversed && currentClip.wrapMode == tk2dSpriteAnimationClip.WrapMode.Loop) {
if (currFrame > _previousFrame) { // wrap around
ProcessEvents (_previousFrame, 0, -1); // back to beg of clip
ProcessEvents (currentClip.frames.Length, currFrame, -1); // process back to current frame
} else {
ProcessEvents (_previousFrame, currFrame, -1);
}
} else {
if (currFrame < _previousFrame) { // wrap around
ProcessEvents (_previousFrame, currentClip.frames.Length - 1, 1); // up to end of clip
ProcessEvents (-1, currFrame, 1); // process up to current frame
} else {
ProcessEvents (_previousFrame, currFrame, 1);
}
}
break;
}

case tk2dSpriteAnimationClip.WrapMode.LoopSection:
{
int currFrame = (int)clipTime;
int currFrameLooped = currentClip.loopStart + ((currFrame - currentClip.loopStart) % (currentClip.frames.Length - currentClip.loopStart));
if (currFrame >= currentClip.loopStart) {
SetFrameInternal (currFrameLooped);
currFrame = currFrameLooped;
if (_previousFrame < currentClip.loopStart) {
ProcessEvents (_previousFrame, currentClip.loopStart - 1, 1); // processed up to loop-start
ProcessEvents (currentClip.loopStart - 1, currFrame, 1); // to current frame, doesn't cope if already looped once
} else {
if (currFrame < _previousFrame) {
ProcessEvents (_previousFrame, currentClip.frames.Length - 1, 1); // up to end of clip
ProcessEvents (currentClip.loopStart - 1, currFrame, 1); // up to current frame
} else {
ProcessEvents (_previousFrame, currFrame, 1); // this doesn't cope with multi loops within one frame
}
}
} else {
SetFrameInternal (currFrame);
ProcessEvents (_previousFrame, currFrame, 1);
}
break;
}

case tk2dSpriteAnimationClip.WrapMode.PingPong:
{
int currFrame = (currentClip.frames.Length > 1) ? ((int)clipTime % (currentClip.frames.Length + currentClip.frames.Length - 2)) : 0;
int dir = 1;
if (currFrame >= currentClip.frames.Length) {
currFrame = 2 * currentClip.frames.Length - 2 - currFrame;
dir = -1;
}
// This is likely to be buggy - this needs to be rewritten storing prevClipTime and comparing that rather than previousFrame
// as its impossible to detect direction with this, when running at frame speeds where a transition occurs within a frame
if (currFrame < _previousFrame) {
dir = -1;
}
SetFrameInternal (currFrame);
ProcessEvents (_previousFrame, currFrame, dir);
break;
}

case tk2dSpriteAnimationClip.WrapMode.Once:
{
int currFrame = (int)clipTime;
if (currFrame >= currentClip.frames.Length) {
SetFrameInternal (currentClip.frames.Length - 1); // set to last frame
state &= ~State.Playing; // stop playing before calling event - the event could start a new animation playing here
ProcessEvents (_previousFrame, currentClip.frames.Length - 1, 1);
OnAnimationCompleted ();
} else {
SetFrameInternal (currFrame);
ProcessEvents (_previousFrame, currFrame, 1);
}
break;
}
}
}

Usage: Once set up the usage is very simple. The following code reverses the animation when you press the middle mouse button.
Code: [Select]

// Script attached to GameObject with a tk2dSpriteAnimator component attached.
void Update () {
tk2dSpriteAnimator animator = GetComponent<tk2dSpriteAnimator>();
if (Input.GetMouseButtonDown (2)) {
animator.reversed = !animator.reversed;
}
}

Just copy and paste the code above into your tk2dSpriteAnimator.cs script. DISCLAIMER: Use at your own risk, there is no guarantee that this code will continue to work in future versions of tk2d.

Pages: [1]