I'm working on an augmented Reality project in Unity where the user will be able to see an animated sprite on their phone when pointing the camera at a certain image.
The Sprite will be created by a belgian comic book artist and will be quite lengthy. To make my life easier I wanted to create an editor script that takes all the sprites from the Resources/Sprite folder and creates an AnimationClip.
Sprite[] sprites = Resources.LoadAll<Sprite>("sprite");
AnimationClip animClip = new AnimationClip();
animClip.frameRate = 25; // fps
EditorCurveBinding spriteBinding = new EditorCurveBinding();
spriteBinding.type = typeof(SpriteRenderer);
spriteBinding.path = "";
spriteBinding.propertyName = "m_Sprite";
ObjectReferenceKeyframe[] spriteKeyFrames = new ObjectReferenceKeyframe[sprites.Length];
for (int i = 0; i < (sprites.Length); i++)
{
spriteKeyFrames[i] = new ObjectReferenceKeyframe();
spriteKeyFrames[i].time = i / animClip.frameRate;
spriteKeyFrames[i].value = sprites[i];
}
AnimationUtility.SetObjectReferenceCurve(animClip, spriteBinding, spriteKeyFrames);
AnimationClipSettings animClipSett = new AnimationClipSettings();
animClipSett.loopTime = true;
AnimationUtility.SetAnimationClipSettings(animClip, animClipSett);
AssetDatabase.CreateAsset(animClip, "assets/generated.anim");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
This works, an AnimationClip is created but when using it in the Animation Panel nothing happens.
I made the same Animation manually to see if that would work and it does.
The blue bar in the animation panel moves though
Animator Panel showing the generated AnimationClip:
The only noticable difference I've found (besides one of them playing and the other one doesn't) is that the generated clip's Length changes to 0 after trying to run it's animation
The Sprite Object and inspector:
I'be been trying to find what I'm doing wrong for half a day now without getting a single step closer.
I'd like to thank who answers on this question in advance.
Greetings,
Maethorian
Related
I'm attempting to make a level select that requires as little upkeep as possible as I intend on adding updates to add more levels (Unity Scenes) in unity.
To account for this I'm attempting to get the level select to create buttons for each level in the Unity Build settings, and then create a template object from a prefab in which it can map buttons it creates onto.
I have it mostly working, but for some reason, it's mapping the buttons in the wrong order, I'm trying to go down to up and Unity appears to be grabbing the Gameobjects is a random order.
Here is my code:
private Scene[] levels;
private int currentButtonId = 1;
public Transform buttonsHolder;
public GameObject buttonPrefab;
public GameObject buttonSlotsPrefab;
private GameObject[] levelButtonSlots;
private int currentLevelSlot = 0;
private int numSlotsToMove = 0;
private void Start()
{
var sceneCount = SceneManager.sceneCountInBuildSettings;
levels = new Scene[sceneCount];
for (var i = 0; i < sceneCount; i++)
{
// Beginning Setup
levels[i] = SceneManager.GetSceneByBuildIndex(i);
// Look for Level Placement Slots
levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot");
// If there aren't enough Level Placement Slots make more by creating a template
if(levelButtonSlots.Length < levels.Length)
{
GameObject buttonSlots = Instantiate(buttonSlotsPrefab);
buttonSlots.transform.position = new Vector2(0, 10 * numSlotsToMove);
numSlotsToMove++;
}
// Go get those new placement slots
levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot");
// Create Button
GameObject currentButton = Instantiate(buttonPrefab, buttonsHolder);
// Move it to the next slot
currentButton.transform.position = levelButtonSlots[currentLevelSlot].transform.position;
currentLevelSlot++;
// Add Text to a new button
TextMeshProUGUI buttonText = currentButton.GetComponentInChildren<TextMeshProUGUI>();
buttonText.text = (currentButtonId.ToString());
// Setup what which scene clicking a button will do
ButtonManager buttonScript = currentButton.GetComponentInChildren<ButtonManager>();
buttonScript.sceneToLoad = currentButtonId;
currentButtonId++;
}
}
The buttonsHolder variable is set in the editor and is the canvas.
The buttonPrefab is a TextMeshPro button prefab I set in the editor, it has the level Buttons tag and a simple script that loads the specified scene when clicked.
And the buttonSlotsPrefab is a gameobject prefab that I set in the editor, it has the Button Placement Slot tag and it contains 8 other empty gameobjects each with the level slot tag, I use these 8 objects as the guides as to where the buttons should be placed on runtime.
Again, my goal is to place the buttons going from bottom to top, but instead, Unity spits out this on runtime for no apparent reason:
I'm sorry about the naming convention some variables have, I'm tired and stressed as I've been at this 2 two days straight. I will fix those up once I get things working.
After further testing, I have noticed that when I first create buttonSlotPrefab, everything works perfectly, however, after restarting Unity (Not changing any files) when I run the game again after a restart the order gets randomized. Could this be a bug in the Unity Engine?
If i understant your problem, you have a problem between number of button and slot: you have slot spaced on axis y and you havent always the levelsolt[0] at the bottom of screen, so
you could order (ascending or descending i dunno) the levelslot on y axis before creating button: i am using Linq so add using System.Linq;
if (levelButtonSlots.Any())//Same thing than levelButtonSlots.Length > 0
{
levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot").OrderBy(go => go.transform.position.y).ToArray();
}
or
levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot").OrderByDescending(go => go.transform.position.y).ToArray();
I have added a test if no value in array, but maybe the error doesnt exist, i havent tested that
i have created a game, and im trying to get the image of the enemies once they have been shot to chnage to a explosion gif before they dissapear but i cant seem to get it to work, you can see the code below where it is meant to change to gif but when you pay the gamethe nemies just dissapear without the gif showing
lasers.RemoveAll(laser => laser.isDisposed);
foreach (Laser laser in lasers)
{
laser.MoveLaser(this);
foreach (Invader ship in invaders)
{
if (laser.laser.Bounds.IntersectsWith(ship.ship.Bounds))
{
laser.isDisposed = true;
laser.laser.Dispose();
ship.ship.Image = SpaceInvaders.Properties.Resources.explosionGIF1;
ship.isDisposed = true;
ship.ship.Dispose();
score = score + 2; //adds 2 points to players score if enemy is hit
lblScore.Text = score.ToString(); //updates the score label
As #jimi explained
Well, you set an Image to the property of an object, then dispose of the object that should show it. Btw, don't try to set [object].isDisposed = true;, it's read-only. Just dipose of an object when needed. Btw2, you need to assign Properties.Resources.[Some Image] to a reusable Image object: right now you're generating a new Image each time.
You're disposing the ship and with that the image.
An example solution would be to create a new GIF object that self-disposes after it has been played once. When the ship is destroyed, a new GIF object is created in the same place the ship was.
I have placed some trees onto the terrain via editor. I have an object which is passing throuh trees and when this object collides with certain tree I want to change its vertices.
void OnTriggerEnter (Collider col) {
DestroyableTree tree = col.gameObject.GetComponent<DestroyableTree>();
if (tree == null)
return;
Mesh mesh = tree.GetComponent<MeshFilter>().mesh;
Vector3[] vertices = mesh.vertices;
for (var i = 0; i < 100; i++){ // 100 just random number, as well as 50 bellow
vertices[i].x = 50;
vertices[i].z = 50;
}
mesh.vertices = vertices;
mesh.RecalculateBounds ();
mesh.RecalculateNormals ();
tree.GetComponent<MeshFilter> ().mesh = mesh;
//tree.UpdadeMesh(tree, mesh);
//tree.Delete();
}
DestroyableTree is from http://rene.klacan.sk/unity3d/games/2014/10/28/destroyable-terrain-trees-in-unity/
When uncommented tree.Delete() lets me be sure that I was trying to edit correct tree.
When I debug I can see that vertices of mesh have changed but why they won't update during runtime of game scene?
EDIT: it is probably because I am accesing capsule component's mesh but not of the tree itself. Still how do I access mesh of trees on the terrain (not necessarily with the code displayed above)?
Using code provided in the link above and having it modified a bit worked. Although it didn't solve the very root issue why I had to ask this question in the first place :(
So here it is how I did it, simple: in the class DestroyableTree that René Klačan wrote I added additional field public TreeInstance treeInstance; which gets initialized after marked statement (in the picture bellow) by adding tree.treeInstance = treeInstance;:
After that I had to reach mesh of the tree using tree.treeInstance.GetComponent<MeshFilter>().mesh; in my code at the beggining of the question. I hope someone will find this useful.
I made this game with Python+Pygame, but it was slow, so I tried it with C# and its forms. It's even slower! I get only 20fps on a i5 radeon 6770m NTB millions of times faster than hardware I want this game to run and I haven't even finished the game yet, it's only rendering a map. It's a remake of a game that was run with slow several MHz processors. It's map containes 400x200 tiles and camera shows only 79*79. I also installed unity 4.5; is it worth learning it, would it bring me significant perfomance increase - if someone knows it how would I do a map consisting of 600*400 tiles randomly either dark tile or bright; it has to be "collidable"? Or I doing sth wrong in forms?
public void render()
{
Bitmap frame = new Bitmap(Game.CANVAS_WIDTH, Game.CANVAS_HEIGHT);
Graphics frameGraphics = Graphics.FromImage(frame);
TextureID[,] textures = Background.Blocks;
while (true)
{
//frameGraphics.FillRectangle(new SolidBrush(Color.Aqua), 0, 0, Game.CANVAS_WIDTH, Game.CANVAS_HEIGHT);
for (int x = 0; x < Game.AR_WIDTH; x++)
{
int xx = x * Game.TILE_SIDE - game.green_tank_pos[0] + Game.DIFF;
if (xx < 0) continue;
if (xx > Game.CANVAS_WIDTH) break;
for (int y = 0; y < Game.AR_HEIGHT; y++)
{
int yy = y * Game.TILE_SIDE - game.green_tank_pos[1] + Game.DIFF;
if (yy < 0) continue;
if (yy > Game.CANVAS_HEIGHT) continue;
switch(textures[x,y])
{
case TextureID.dark:
frameGraphics.DrawImage(tex_dark_gnd, xx, yy);
break;
case TextureID.bright:
frameGraphics.DrawImage(tex_bright_gnd, x * Game.TILE_SIDE - game.green_tank_pos[0] + Game.DIFF, y * Game.TILE_SIDE - game.green_tank_pos[1] + Game.DIFF);
break;
}
}
}
frameGraphics.DrawImage(tex_green_tank, Game.DIFF, Game.DIFF);
drawHandle.DrawImage(frame, 0, 0);
WinForms is a terrible platform for a game, even a 2D one. It was intended for line-of-business applications, and includes little to no hardware acceleration.
Plus the technology is just hard to write a game in, let alone an efficient one. You might get away with using WPF for a very simple game, but you will really want to learn XNA, MonoGame, Unity or some other actual game platform that can take advantage of DirectX (WPF does this as well, btw).
There are ways to get better performance drawing in winforms but for a game it's best to go with tools that better fit the job. (don't use a screwdriver to hit a nail).
The newer versions of Unity3D have built-in 2D tools. I'm personally building a 2D game in it and would highly recommend it.
EDIT: Removed my mention of XNA - I wasn't aware it was "dead".
Creating tiles in Unity3D can be done several ways
Place sprites into the scene manually. Their locations and settings will be saved along with the scene.
Place the sprites within the scene manually and create a prefab out of it. Thus, that set of tiles can be reused.
Create a prefab of a single tile, and instantiate multiples of that prefab in a Behavior that is attached somewhere in a scene.
As for the rendering Unity3D will take care of that for you.
EDIT2: I created a short Unity3D behavior that you can attach somewhere inside your scene. This uses the 3rd approach I outlined above and it will pick randomly from a set of prefabs applied. This script assumes randomly picking from tiles and that the width of the tiles are 1 game unit wide/high.
Attach the behavior to something in the scene (like the main camera)
Create your tiles as prefabs
Drag the prefabs in the editor into the "Tile Prefabs" array on the behavior you've attached.
Set the "Tiles High" to 400 and "Tiles Wide" to 600
Set TileMapTopLeft to the top-left position you want it to start.
Run your scene.
Here's the behavior:
using UnityEngine;
class TileCreator : MonoBehaviour
{
private static System.Random rng = new System.Random();
public GameObject[] TilePrefabs;
public int TilesWide;
public int TilesHigh;
public Vector3 TileMapTopLeft;
void Start()
{
for (int x = 0; x < TilesWide; x++)
{
for (int y = 0; y < TilesHigh; y++)
{
Instantiate(TilePrefabs[rng.Next(TilePrefabs.Length)], new Vector3(x + TileMapTopLeft.x, y + TileMapTopLeft.y, TileMapTopLeft.z), Quaternion.identity);
}
}
}
}
You'll probably need to look up a tutorial on how to import resources and create prefabs but this script should get you in the right direction.
I'm having some discrepancy between playing my unity project inside the editor, and playing the build it makes.
Inside the editor, when I play my game I have code that spawns enemies when my player hits a target spot, which works. The enemies spawn and do what they need to do. However, when I build the project and run it, my player hits the same spot, but nothing appears.
I ran the project in the development mode and it claims I have not set the reference which I thought I had. I mean, if it didn't work, why is it working in the editor?
To try and combat this, I re-imported the assets I'm using. I remade the enemy spawn points. But I still get the same issue.
Has anyone ever came across this before?
If so, how can I fix this issue when it works inside the editor, but not in the build?
Here is my spawning function:
public GameObject SpawnEnemies()
{
Vector3 _position = new Vector3(transform.position.x, transform.position.y, transform.position.z);
// instantiate particel system
// Instantiate(_particle, _position, Quaternion.identity);
audio.PlayOneShot(_appearSound);
_clone = (GameObject)Instantiate(_enemy, _position, transform.rotation);
_ai = _clone.GetComponent<HV_BaseAI>();
_ai._waypoints = _wayPoints;
return _clone;
}
This is controlled by my section class which has the following function:
public List<GameObject> SpawnGroundEnemies()
{
List<GameObject> groundObjectList = new List<GameObject>();
for (int i = 0; i < _spawnPoints.Count; i++)
{
groundObjectList.Add(_spawnPoints[i].SpawnEnemies());
}
return groundObjectList;
}
Which, in turn is controlled by my section controller:
GameObject g, f;
g = GameObject.FindGameObjectWithTag("SectionController");
f = GameObject.FindGameObjectWithTag("SectionController");
HV_SectionController tempSectionControllerGroundEnemies, tempSectionControllerFlyingEnemies;
tempSectionControllerGroundEnemies = g.GetComponent<HV_SectionController>();
tempSectionControllerFlyingEnemies = f.GetComponent<HV_SectionController>();
_groundEnemiesRemaining = tempSectionControllerGroundEnemies._numberOfGroundEnemies.Count;
_flyingEnemiesRemaining = tempSectionControllerFlyingEnemies._numberOfFlyingEnemies.Count;
_enemiesRemaining = _groundEnemiesRemaining + _flyingEnemiesRemaining;
if (!_moving)
{
if (_enemiesRemaining == 0)
{
MoveToNextSection();
}
}
if (_moving)
{
if (Vector3.SqrMagnitude(_camera.transform.position - _camNavMesh.destination) < 5.0f)
{
_moving = false;
// spawn the next set of enemies
// need this for each section of the game
// without this, no enemies will spawn
if (CurrentSection == 1)
{
_numberOfGroundEnemies = _sections[0].SpawnGroundEnemies();
_enemiesRemaining = _numberOfGroundEnemies.Count;
}
if (CurrentSection == 2)
{
_numberOfFlyingEnemies = _sections[1].SpawnFlyingEnemies();
_enemiesRemaining = _numberOfFlyingEnemies.Count;
}
if (CurrentSection == 3)
{
_numberOfGroundEnemies = _sections[2].SpawnGroundEnemies();
_enemiesRemaining = _numberOfGroundEnemies.Count;
}
}
}
}
private void MoveToNextSection()
{
if (_currentSection == _sections.Count)
{
_currentSection = 0;
}
_camNavMesh.SetDestination(_sections[_currentSection]._cameraTransform.position);
_lookAtPoint.SetTimer(_sections[_currentSection]._cameraLookAtTarget.position);
_moving = true;
_currentSection += 1;
}
Can anyone see anything i'm doing wrong?
Update
Since this post, I've tried reimporting all of my assets again. I've even created a fresh project and done a fresh build that way, but I still have the same issue. Also, to help, I've take two screen grabs to show what I mean.
First off, the game running in the unity editor:
As you can see, there is 4 skeletons on the bridge. These guys appear when I hit play in the unity editor.
Now, when I build the project, this is same sections:
As you can see, no enemies, but when its a development build, it says I've no reference set. In the output log unity genereates, it gives me this message:
NullReferenceException: Object reference not set to an instance of an
object at HV_SpawnGroundEnemy.SpawnEnemies () [0x00052] in
C:\Users\sean.obrien\Desktop\Medieval Darkride
Level\Assets_scripts\Enemy Scripts\HV_SpawnGroundEnemy.cs:37
at HV_Section.SpawnGroundEnemies () [0x0000d] in
C:\Users\sean.obrien\Desktop\Medieval Darkride
Level\Assets_scripts\Helper Scripts\HV_Section.cs:26
at HV_SectionController.Update () [0x000c2] in
C:\Users\sean.obrien\Desktop\Medieval Darkride
Level\Assets_scripts\Helper Scripts\HV_SectionController.cs:72
Again, this only appears in the built version of the game. When I run this inside Unity, everything works fine. I honestly have no idea why this issue arises when it works fine in the editor but not in the build.
There's most probably two cases here. Either you've not saved your scene yet, or you're not including the correct scene while making the build.
If it's the first case, just save the scene. If it's second, then you need to choose proper scene in the build settings and then rebuild.
Explanation:
You might have saved the scene as some other file, but the build setting does not update the scene file dynamically. It'll still point to the old scene. So you just need to remove the old scene and add the current scene you're using.