I'm trying to make a prototype for my interactive media class but I hit a little hiccup on the progress. I was following a tutorial where everything was running smoothly till I got to using the Animator. I followed every instruction step by step during the tutorial I was watching. Basically, my 2D Sprite Character is stuck in the fall animation whenever I play the game rather than it being in the default idle animation like it's supposed to be. I tried deleting and recreating the animation paths but that didn't work. I even tried deleting everything and putting everything in from scratch back into the animator. I did check off exit time, set the duration for zero, and give the "state" which is what I called the in their respective numbers but it's still stuck on falling. when I jump with my character the run and idle animation seem to be working. It's like everything reversed with falling. It also gave me the difference in effective length is too big as an error. I tried adding more frames to my falling animation to see if that would fix and also tried checking and unchecking loop time and It's still stuck on the fall. If anyone knows what's wrong with the animator please let me know. I don't think it has anything to do with the code but better safe than sorry so I'll put it here. If anyone has the answer to my issue please get back to me when you can and thank you!
PlayerMovement.cs: `
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private Rigidbody2D rb;
private SpriteRenderer sprite;
private Animator anim;
private float dirX = 0f;
[SerializeField] private float moveSpeed = 12f;
[SerializeField] private float jumpForce = 16f;
private enum MovementState { idle, running, jumping, falling }
// Start is called before the first frame update
private void Start()
{
rb = GetComponent<Rigidbody2D>();
sprite = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
}
// Update is called once per frame
private void Update()
{
dirX = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y);
if (Input.GetButtonDown("Jump"))
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}
UpdateAnimationState();
}
private void UpdateAnimationState()
{
MovementState state;
if (dirX > 0f)
{
state = MovementState.running;
sprite.flipX = false;
}
else if (dirX < 0f)
{
state = MovementState.running;
sprite.flipX = true;
}
else
{
state = MovementState.idle;
}
if (rb.velocity.y > .1f)
{
state = MovementState.jumping;
}
else if (rb.velocity.y > -.1f)
{
state = MovementState.falling;
}
anim.SetInteger("state", (int)state);
}
}
This is my Animator in Unity
The Player Falling Inspector
Player Running -> Player Falling
Player Idle -> Player Falling
Player Jumping -> Player Falling
Related
I'm doing some tests with DotNet 5rc1 and Blazor, and have noticed something that makes me think I'm not doing this right.
I have a demo page (code below) that gives the user a set of buttons and they can click on a button to do a die roll. In the client side C# I then roll the die and prepend the roll string to a list. In the template, I then do a foreach and render the user's past rolls.
The issue is that I've been watching the Websocket messages and each time I add a new element, the message being passed gets larger and larger. This is because the entire list is being re-rendered with each click.
Is there a better way to do this, so that Blazor only inserts the new item in the DOM and sends down a trivial message, rather than re-rendering the entire section of the page? For this example, it doesn't matter much, but if I were building a bigger app with a lot of interactions, I could see this becoming a bottleneck.
Here's the code for my page - this is just a new page in a brand new Blazor project. (My code has more than a 1d20 button, but I removed it from here for brevity.)
#page "/dieroller"
<h1>Die Roller Utility</h1>
...
<button class="btn btn-primary" #onclick="#(e => RollDie(20))">
<i class="fas fa-dice-d20"></i>
</button>
<h2 class='mt-5'>Your Roll History</h2>
#foreach (string roll in rolls) {
<p>#roll</p>
}
#code {
#using System.Threading;
private List<string> rolls = new List<string>();
private void RollDie(int upper)
{
Random rand = new Random();
rolls.Insert(0, string.Format("1d{0} = {1}", upper, rand.Next(1, upper)));
}
}
The direct answer to your problem is to use #key. That is always a good idea when a loop is involved.
The problem is that you need unique keys and your strings don't fit that requirement.
A blunt solution is to introduce a class (not a struct), just to get unique keys. I adapted your code to the following:
#foreach (Roll roll in rolls)
{
<p #key="roll">#($"d={roll.Upper} {roll.Value}")</p>
}
#code {
class Roll
{
public int Upper { get; set; }
public int Value { get; set; }
}
private List<Roll> rolls = new List<Roll>();
private void RollDie(int upper)
{
Random rand = new Random();
rolls.Insert(0, new Roll { Upper = upper, Value = rand.Next(1, upper + 1) });
}
}
Using this you do get stable, non-growing WS packets.
Based on #BrianParker's comment above - Yes, you need a key so that Blazor knows which items need to be updated. That Key also needs to be unique, or you'll generate errors with key collision.
Because these are strings, they don't make good keys - therefore I ended up retooling this with a DieRoll class that I could put in a list. Here's the new code behind:
using System;
using System.Collections.Generic;
namespace BlazorApp.Pages
{
public partial class DieRoller
{
private List<DieRoll> rolls = new List<DieRoll>();
private void RollDie(int upper)
{
rolls.Insert(0, new DieRoll(upper));
}
}
public class DieRoll
{
public int Roll;
public int DieType;
public DieRoll(int DieType) {
this.DieType = DieType;
RollDie();
}
public int RollDie() {
Random rand = new Random();
this.Roll = rand.Next(1, DieType + 1);
return Roll;
}
public override string ToString()
{
return string.Format("1d{0} = {1}", this.DieType, this.Roll);
}
}
}
And here's the new template code at the p tag:
#foreach (DieRoll die in rolls) {
<p #key="die">#die</p>
}
Obviously this is more complex, but this is what works. The messages from the server are much smaller, and never grow in size.
Also, this might NOT have mattered if I wasn't prepending the items to the List. Appending to the list might have let Blazor understand where the elements were being created easier. But I didn't bother to test that theory.
You can see another example here: https://blazor-university.com/components/render-trees/optimising-using-key/
With List.Add the messages size remains the same, so why is List.Insert any different? Presumably it's due to the inherent performance penalty that List.Insert(index: 0) incurs since the underlying array needs to be reshuffled, see here: https://stackoverflow.com/a/18587349/4000335
To verify this even using List.Insert(List.Count) shows the message size remains the same (as is the case with Add).
If the requirement is to have the last roll on top then an optimal approach would probably involve JavaScript, which defeats the purpose of using blazor server in the first place, however, blazor webassembly doesn't need to communicate with a server to accomplish this since it's all done on the client.
I am working on small game in JavaFX.
I have an utility class to manage music:
private static Map<SongEnum, Media> songs = new EnumMap<>(SongEnum.class);
private static MediaPlayer currentSong;
public static void playSong(SongEnum song) {
if(songs == null && currentSong != null) {
currentSong.stop();
currentSong = null;
}
Media media = songs.get(song);
if(media == null) {
String path = ApplicationUtils.class.getResource(song.getPath()).toExternalForm();
media = new Media(path);
songs.put(song, media);
}
if(currentSong != null) {
if(currentSong.getMedia() == media)
return;
currentSong.stop();
}
currentSong = new MediaPlayer(media);
currentSong.setCycleCount(MediaPlayer.INDEFINITE);
currentSong.play();
}
I want to be able to play many different songs - is above approach correct?
Also I am not sure how to implement volume-management system in this case. My first thought was to bind some slider property (which lies in another class) with MediaPlayer property, but in this case it will change everytime the songs changes. So, what is the best way to achieve it?
In the game we're currently working on, we just used the volume property of the MediaPlayer. We put 0.3 on the background theme, and 0.8or 1on effects as they should be higher than the background theme. Test it out see how it works best by using currentSong.setVolume("0 to 1");. As of using a slider, why don't just use a setOnEndOfMediato loop the song. With this, the volume shouldn't change. This only works if you of course are only looping the same song.
currentSong.setOnEndOfMedia(() -> {
currentSong.seek(Duration.ZERO);
currentSong.play();
});
If not, I would add make currentSong static, and then access it like "YourMediaClass.currentSong.setVolume("slider.getSliderValue or whatever you use"). This is probably what you're looking for.
I'm new to cocos2d and coding and I'm sure this question was asked many times already, but I want to animate a ghost in a tilemap to go up about 150 pixels when the player is next to to it. I have
`CCSprite* sprite1 = [CCSprite spriteWithFile:#"enemy.png"];
sprite1.position = ccp(464, 80);
[self addChild:sprite1];
[sprite1 runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:1 position:ccp(0, 150)],
[CCMoveBy actionWithDuration:1 position:ccp(0, -200)], nil]];`
which animates a sprite but it stays on the map. If I add it to a tilemap it will only show when the player is close. But I'm not exactly sure how to do that. Thanks in advance
Don't get it on a tilemap then, you're coding custom behavior for that specific character, work it apart from the TileMap. You'll have to work on sensors if you're planning on getting efficient code, maybe Box2D or the built in Chipmunk engine that CocosV3 has recently fully integrated, or an "easy" way out of this, if you're not planning on making a sliding scenario you could work simple coordinates and an event listener on the update method, so when the "character" reaches the point you wish the ghost to appear, well then you can make that method happen.
Wrap the custom behavior on a class that you may further reuse , maybe a class named sensorSprite , once you code the default behavior for the class, you could create methods to instantiate the object with specific coordinates to generate the sensor around, or some other cool stuff.
Here's what your class could look like.
Header File
#interface SensorSprite : CCSprite {
kCharacterState currentState;
}
-(id)initWithSpriteFrame:(CCSpriteFrame *)spriteFrame inCoordinates:(CGPoint)spawnLocation withParent:(id)theParent;
-(void)updateSensorSpriteWithCharacters:(NSMutableArray*)charactersArray;
#end
Implementation File
typedef enum {
kCharacterStateAlive,
kCharacterStateDead,
kCharacterStateAppearing,
kCharacterStateDissapearing
}kCharacterState;
#import "SensorSprite.h"
#implementation SensorSprite
-(id)initWithSpriteFrame:(CCSpriteFrame *)spriteFrame inCoordinates:(CGPoint)spawnLocation withParent:(id)theParent{
self = [CCSprite spriteWithImageNamed:#"enemy.png"];
self.position = ccp(464, 80);
[theParent addChild:self];
return self;
}
-(void)updateSensorSpriteWithCharacters:(NSMutableArray *)charactersArray {
//If you're planning on having different characters to update your state from then you should use tags.
for (int i=0; i<=charactersArray.count-1; i++) {
CCSprite *characterInArray = [charactersArray objectAtIndex:i];
if (CGRectIntersectsRect([characterInArray boundingBox], [self boundingBox])) {
//What happens when the CG Rects from this class and another one intersects.
//For making reactive to encountering different sprites, you should use tags , and on each cycle detect what sprite is the one colliding by looking at it's tag.
}
}
}
-(void)changeStateTo:(kCharacterState)theState {
if (currentState==theState) {
//It's the same state.
return;
}
switch (theState) {
case kCharacterStateAlive:
//What happens when character state alive.
break;
case kCharacterStateDead:
//What happens when character state dead
break;
case kCharacterStateAppearing:
//What happens when character state reapearing
break;
case kCharacterStateDissapearing:
//What happens when character state dissapearing.
default:
break;
}
//After doing the required we set our current state to the changed state.
currentState = theState;
}
#end
Notice my comments on the code, it could be improved in huge ways, but this should be your base to understand what's going on here.
I need to push real time data to a Flex GUI (Data grid), but each time new data is pushed to the grid, it's losing its previous state.
Example:
if I scrolled to the right, after the next update scrolls come back to the default position, that is, left
if I am selecting any row, it's getting unselected just after update.
Is there a way to maintain the state?
I am using Flex 3. I can move to Flex 4 if it helps.
How do you set the data? Do you change the DataGrid dataProvider with a new collection of objects ? Because, from the behavior described by you this may be the case.
The solution would be instead changing the dataProvider of the DG. Each time, you should just update the values in the collection which is assigned as data provider.
For example,
[Bindable]
var myDataCollection:ArrayCollection = new ArrayCollection([0,1,2,3,4]);
// Handle creation complete.
private function onCreationComplete():void
{
initDG();
}
// Init DG data provider just once.
private function initDG(data:ArraCollection):void
{
myDG.dataProvider = data;
}
private function updateDG_Method_1(row:int, value:int):void
{
var data:ArrayCollection = myDG.dataProvider as ArrayCollection;
if(data && data.length > row)
{
data[row] = value;
}
// We can force refresh if not not done automatically.
myDG.invalidateList();
myDG.validateNow();
}
private function updateDG_Method_2(row:int, value:int):void
{
if(myDataCollection && myDataCollection.length > row)
{
myDataCollection[row] = value;
}
// We can force refresh if not not done automatically.
myDG.invalidateList();
myDG.validateNow();
}
Please ignore/correct any typo .. since I did not test this :))
Good luck!
We need to be able to handle a "playable" (play/pause/seek) effect in which the nature of the effect cannot be determined at compile time.
The problem we are running into is resetting the target(s) state after the effect has completed. If we manually drag the seek slider back to the beginning, everything works fine. However, if we set the playheadTime of the composite effect back to 0, the effected targets retain their original value until the playheadTime gets to the correct position to effect the target.
Here is a simplified (as much as I could) test case with view source enabled:
http://www.openbaseinteractive.com/_tmp/PlayableEffectTest/
The problem is demonstrated if you let it play to the end, and then hit the play button to start it over.
What is the best way to go about manually resetting the target values given that the exact nature of the effect is unknown?
Many thanks for your time!
edit
I forgot to mention we are using Flex 4.5 preview release.
Have you tried:
effect.reverse()
More info
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/effects/IEffect.html
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/effects/IEffect.html#reverse()
Well it's a little kludgy, but I was able to accomplish this by calling some internal methods on the effect to capture the start values, then assigned those values to the targets on a reset.
import mx.core.mx_internal;
use namespace mx_internal;
private var _propertyChangesArray:Array;
protected function captureStartValues(effect:Object):void
{
effect.captureStartValues();
_propertyChangesArray = effect.propertyChangesArray;
}
protected function reset(effect:Object):void
{
for each(var change:PropertyChanges in _propertyChangesArray)
{
var target:Object = change.target;
for(var p:String in change.start)
{
if(target.hasOwnProperty(p))
{
var startVal:* = change.start[p];
var endVal:* = target[p];
if(!isNaN(startVal) && startVal != endVal)
{
target[p] = startVal;
}
}
}
}
effect.playheadTime = 0;
}
I don't know if this is the best way to accomplish this, but it seems to be working so far. I am absolutely open to suggestions for a better method.
Cheers!