Skip to content

Instantly share code, notes, and snippets.

@mohiji
Created March 17, 2018 04:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mohiji/f3f395a0bb0e6ad1c3495f1c448e3ef7 to your computer and use it in GitHub Desktop.
Save mohiji/f3f395a0bb0e6ad1c3495f1c448e3ef7 to your computer and use it in GitHub Desktop.
Components and delegates
@import GameplayKit;
@import SpriteKit;
@class AnimatedSpriteComponent;
@protocol AnimatedSpriteComponentDelegate
- (void)animatedSpriteComponent:(AnimatedSpriteComponent *)component didUpdateToFrame:(SKTexture *)frame;
- (void)animatedSpriteComponent:(AnimatedSpriteComponent *)component didCompleteAnimation:(NSString *)animationName;
@end
@interface AnimatedSpriteComponent : GKComponent
@property (weak, nonatomic) id<AnimatedSpriteComponentDelegate> delegate;
@property (assign, nonatomic) int framesPerSecond;
@property (readonly, nonatomic) float secondsPerFrame;
@property (readonly, nonatomic) NSString *currentAnimation;
- (void)addAnimationNamed:(NSString *)name withFrames:(NSArray<SKTexture *> *)frames;
- (void)playAnimation:(NSString *)name looping:(BOOL)looping;
- (void)stopAnimation;
@end
#import "AnimatedSpriteComponent.h"
static const int kDefaultFramesPerSecond = 5;
@interface AnimatedSpriteComponent () {
int _currentFrame;
CFTimeInterval _timeThisFrame;
BOOL _looping;
BOOL _animating;
}
@property (strong, nonatomic) NSMutableDictionary<NSString *, NSArray<SKTexture *> *> *animations;
@property (copy, nonatomic) NSString *currentAnimation;
@end
@implementation AnimatedSpriteComponent
- (id)init {
self = [super init];
if (self != nil) {
self.animations = [NSMutableDictionary dictionary];
_framesPerSecond = kDefaultFramesPerSecond;
}
return self;
}
- (float)secondsPerFrame {
return 1.0f / _framesPerSecond;
}
- (void)addAnimationNamed:(NSString *)name withFrames:(NSArray<SKTexture *> *)frames{
self.animations[name] = frames;
}
- (void)playAnimation:(NSString *)name looping:(BOOL)looping{
NSArray *animation = self.animations[name];
if (animation == nil) {
return;
}
self.currentAnimation = name;
_currentFrame = 0;
_timeThisFrame = 0.0;
_looping = looping;
_animating = YES;
[self.delegate animatedSpriteComponent:self didUpdateToFrame:animation[_currentFrame]];
}
- (void)updateWithDeltaTime:(NSTimeInterval)seconds {
if (!_animating) {
return;
}
_timeThisFrame += seconds;
float secondsPerFrame = [self secondsPerFrame];
if (_timeThisFrame >= secondsPerFrame) {
_currentFrame++;
_timeThisFrame -= secondsPerFrame;
NSArray *animation = self.animations[self.currentAnimation];
if (_currentFrame >= [animation count]) {
_currentFrame = 0;
if (!_looping) {
[self stopAnimation];
return;
}
}
[self.delegate animatedSpriteComponent:self didUpdateToFrame:animation[_currentFrame]];
}
}
- (void)stopAnimation {
_animating = NO;
[self.delegate animatedSpriteComponent:self didCompleteAnimation:self.currentAnimation];
}
@end
@import GameplayKit;
@class MovementComponent;
@protocol MovementComponentDelegate
- (CGPoint)currentPositionForMovementComponent:(MovementComponent *)component;
- (void)movementComponent:(MovementComponent *)component didMoveToPosition:(CGPoint)position;
- (void)movementComponent:(MovementComponent *)component didReachTarget:(CGPoint)target;
@end
@interface MovementComponent : GKComponent
@property (weak, nonatomic) id<MovementComponentDelegate> delegate;
@property (assign, nonatomic) CGFloat threshold;
@property (assign, nonatomic) CGFloat speed;
@property (readonly, nonatomic) CGPoint target;
@property (readonly, nonatomic) BOOL moving;
- (void)moveToPosition:(CGPoint)position;
@end
#import "MovementComponent.h"
#import "CGPointMath.h"
static const CGFloat kMovementComponentDefaultThreshold = 1.0f;
static const CGFloat kMovementComponentDefaultSpeed = 90.0f;
@interface MovementComponent ()
@property (assign) CGPoint target;
@property (assign) BOOL moving;
@end
@implementation MovementComponent
- (id)init {
self = [super init];
if (self != nil) {
self.threshold = kMovementComponentDefaultThreshold;
self.speed = kMovementComponentDefaultSpeed;
}
return self;
}
- (void)moveToPosition:(CGPoint)position {
_moving = YES;
_target = position;
}
- (void)updateWithDeltaTime:(NSTimeInterval)seconds {
if (!_moving || _delegate == nil) {
return;
}
CGPoint current = [_delegate currentPositionForMovementComponent:self];
CGPoint dv = CGPointSubtract(_target, current);
if (CGPointMagnitude(dv) < _threshold) {
_moving = NO;
[_delegate movementComponent:self didReachTarget:_target];
return;
}
dv = CGPointNormalized(dv);
CGPoint newPosition = CGPointAdd(current, CGPointScale(dv, seconds * _speed));
[self.delegate movementComponent:self didMoveToPosition:newPosition];
}
@end
@import GameplayKit;
@import SpriteKit;
#import "MovementComponent.h"
#import "AnimatedSpriteComponent.h"
@interface PlayerControllerComponent : GKComponent<MovementComponentDelegate, AnimatedSpriteComponentDelegate>
@property (strong, nonatomic) SKTexture *idleUpTexture;
@property (strong, nonatomic) SKTexture *idleDownTexture;
@property (strong, nonatomic) SKTexture *idleLeftTexture;
@property (strong, nonatomic) SKTexture *idleRightTexture;
- (void)moveToPosition:(CGPoint)position;
@end
#import "PlayerControllerComponent.h"
#import "SpriteComponent.h"
#import "CGPointMath.h"
@implementation PlayerControllerComponent
- (void)moveToPosition:(CGPoint)position {
MovementComponent *m = [self mover];
if (m == nil) {
return;
}
m.delegate = self;
[m moveToPosition:position];
CGPoint current = [self currentPosition];
CGPoint dv = CGPointSubtract(position, current);
AnimatedSpriteComponent *animator = [self animator];
animator.delegate = self;
if (fabs(dv.x) > fabs(dv.y)) {
if (dv.x < 0.0) {
[animator playAnimation:@"walk-left" looping:YES];
}
else {
[animator playAnimation:@"walk-right" looping:YES];
}
}
else {
if (dv.y < 0) {
[animator playAnimation:@"walk-down" looping:YES];
}
else {
[animator playAnimation:@"walk-up" looping:YES];
}
}
}
- (CGPoint)currentPositionForMovementComponent:(MovementComponent *)component {
return [self currentPosition];
}
- (void)movementComponent:(MovementComponent *)component didMoveToPosition:(CGPoint)position {
[self spriteComponent].sprite.position = position;
}
- (void)movementComponent:(MovementComponent *)component didReachTarget:(CGPoint)target {
[self spriteComponent].sprite.position = target;
[[self animator] stopAnimation];
}
- (void)animatedSpriteComponent:(AnimatedSpriteComponent *)component didCompleteAnimation:(NSString *)animationName {
if ([@"walk-up" isEqualToString:animationName]) {
[self spriteComponent].sprite.texture = self.idleUpTexture;
}
else if ([@"walk-down" isEqualToString:animationName]) {
[self spriteComponent].sprite.texture = self.idleDownTexture;
}
else if ([@"walk-left" isEqualToString:animationName]) {
[self spriteComponent].sprite.texture = self.idleLeftTexture;
}
else if ([@"walk-right" isEqualToString:animationName]) {
[self spriteComponent].sprite.texture = self.idleRightTexture;
}
}
- (void)animatedSpriteComponent:(AnimatedSpriteComponent *)component didUpdateToFrame:(SKTexture *)frame {
[self spriteComponent].sprite.texture = frame;
}
- (MovementComponent *)mover {
return (MovementComponent *)[self.entity componentForClass:[MovementComponent class]];
}
- (SpriteComponent *)spriteComponent {
return (SpriteComponent *)[self.entity componentForClass:[SpriteComponent class]];
}
- (AnimatedSpriteComponent *)animator {
return (AnimatedSpriteComponent *)[self.entity componentForClass:[AnimatedSpriteComponent class]];
}
- (CGPoint)currentPosition {
SpriteComponent *s = [self spriteComponent];
if (s == nil) {
return CGPointZero;
}
return s.sprite.position;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment