سؤال

So I'm coming from a bit of a processing background (albiet not much) where I'm used to working with a draw loop that runs over and over when the app is running.

I can't seem to find something similar in xcode and C4. Is there anything like that anyone can suggest?

What I'm basically doing is just creating a bouncing ball app using C4 vectors as properties in a custom class

Here is my custom class Header/Implementation file:

@interface splitSquare : C4Shape

@property C4Vector *accel;
@property C4Vector *velocity;
@property C4Vector *location;

-(id)initWithPoint:(CGPoint)point;

@end

-(id)initWithPoint:(CGPoint)point{
    self = [super init];
    if (self){

        CGRect frame = CGRectMake(0, 0, 50, 50);
        [self ellipse:frame];
        self.lineWidth = 0.0f;
        self.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
                                         green:[C4Math randomInt:100]/100.0f
                                          blue:0.5f
                                         alpha:1.0f];

        _location = [C4Vector vectorWithX:point.x Y:point.y Z:0.0f];

        self.origin = _location.CGPoint;


        _accel = [C4Vector vectorWithX:0.04f Y:0.05f Z:0.0f];
        _velocity = [C4Vector vectorWithX:0.004f Y:0.0005f Z:0.0f];
        C4Log(@"The current value of velocity x is %f and y is %f", _velocity.x, _velocity.y);


}

I'm doing something pretty simple (adding accel to velocity and velocity to location, then assign location to the shapes origin). In the main C4WorkSpace I have this code:

@interface C4WorkSpace()
-(void)updateVectors;
@end

@implementation C4WorkSpace

{
    splitSquare *testShape;
    C4Timer *timer;
}
-(void)setup {

testShape = [[splitSquare alloc] initWithPoint:point2];

        [self.canvas addShape:testShape];
    testShape.animationDuration = 0.25f;

    timer = [C4Timer automaticTimerWithInterval:0.25f target:self method:@"updateVectors" repeats:YES];

}

-(void)updateVectors{
    //accel add to vel, vel added to location
    [testShape.velocity add:testShape.accel];
    [testShape.location add:testShape.velocity];
    testShape.origin = testShape.location.CGPoint;
    testShape.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
                                          green:[C4Math randomInt:100]/100.0f
                                           blue:0.5f
                                          alpha:1.0f];   
}
@end

So this is what I'm doing now with a C4 timer getting called, but I feel like there has to be a more elegant way to do this. Right now the animation is jumpy and although it doesn't throw an error it doesn't seem to work properly (every time I run the iOS simulator it runs for a second then jumps back into xcode and shows me the current thread).

Any/all suggestions would be awesome thanks!

هل كانت مفيدة؟

المحلول

You're actually right on track with using a timer to create an update loop. Instead of having an entire update for the screen, C4 updates only the content it needs to update at any given time. This is why there's no draw loop like in other apis. A major reason for this is to be lighter on system resources (i.e. less drawing / less updating = longer battery life for mobile devices).

To address your issues above, I've slightly modified the code you sent over.

Here's the splitSquare header:

#import "C4Shape.h"

@interface splitSquare : C4Shape

@property C4Vector *accel;
@property C4Vector *velocity;
@property C4Vector *location;

-(id)initWithPoint:(CGPoint)point;
-(void)beginUpdating;
@end

NOTE: I added a beginUpdating method to the header.

Here's the splitSquare implementation:

#import "splitSquare.h"

@implementation splitSquare {
    C4Timer *updateVectorTimer;
}

-(id)initWithPoint:(CGPoint)point{
    self = [super init];
    if (self){

        CGRect frame = CGRectMake(0, 0, 50, 50);
        [self ellipse:frame];
        self.lineWidth = 0.0f;
        self.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
                                         green:[C4Math randomInt:100]/100.0f
                                          blue:0.5f
                                         alpha:1.0f];

        _location = [C4Vector vectorWithX:point.x Y:point.y Z:0.0f];

        self.origin = _location.CGPoint;


        _accel = [C4Vector vectorWithX:0.04f Y:0.05f Z:0.0f];
        _velocity = [C4Vector vectorWithX:0.004f Y:0.0005f Z:0.0f];
        C4Log(@"The current value of velocity x is %f and y is %f",
              _velocity.x,
              _velocity.y);
    }
    return self;
}

-(void)beginUpdating {
    updateVectorTimer = [C4Timer automaticTimerWithInterval:1/60.0f
                                                     target:self
                                                     method:@"updateVectors"
                                                    repeats:YES];
}

-(void)updateVectors{
    //accel add to vel, vel added to location
    [self.velocity add:self.accel];
    [self.location add:self.velocity];
    self.origin = self.location.CGPoint;
    self.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
                                     green:[C4Math randomInt:100]/100.0f
                                      blue:0.5f
                                     alpha:1.0f];
}
@end

NOTE: I added the timer directly to the splitSquare object.

The main thing for making your animation smoother happens in the beginUpdating method when you create the update timer.

Originally, you were animating for 0.25 seconds before updating and triggering the timer every 0.25 seconds as well. I've changed the animation duration so that it is immediate (0.0f seconds).

I then changed the update timer to run at 60fps by triggering the timer at '1/60.0f' seconds so that the animation is smoother.

I made an alternative implementation (just to show that it can be done this way as well) by adding the animation and timer to the splitSquare method itself. The C4WorkSpace changed to the following:

#import "C4WorkSpace.h"
#import "splitSquare.h"

@implementation C4WorkSpace {
    splitSquare *testShape;
}

-(void)setup {
    testShape = [[splitSquare alloc] initWithPoint:CGPointMake(100,100)];
    [self.canvas addShape:testShape];
    [testShape beginUpdating];
}

@end

Instead of managing the animation of the object itself, the workspace simply starts tells the object to beginUpdating.


A bit about what's going on.

One of the main differences, as you know, is that there is no default draw loop. Part of the reason for this is that as the developer you're almost never really dealing with the actual drawing/rendering of objects to the screen. When you create a visual object and add that to the canvas, the rendering is done by the system itself so that drawing is much more efficient on system resources. This is the ideal way of dealing with media not only in C4 but also in iOS programming.

Not having a draw loop is definitely different and takes some getting used to. But, it's really quite a nice way of working.

In essence, the draw-loop in other apis gets triggered by something like a timer every 30 or 60 seconds. So, adding a timer the way you did to update your shape is exactly what you want to do if you need loop-like functionality.

The nice thing about NOT having a timer in C4 is that you can simply add one to your workspace to control the firing of updates. BUT an even nicer thing is that you can add timers to individual objects so that they can update themselves. This means that you can also have multiple timers in multiple objects updating at different times / delays to create a more dynamic set of actions than you could ever get with only a single running loop.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top