i am not really familiar with objC and looking for a solution to build a slider that slides with "steps". on moving the slider the value increases like value+=10 or maybe value+=100. how do i do that?
I modified the code above for a custom Cocos2D slider http://yannickloriot.com/library/ios/cccontrolextension/Classes/CCControlSlider.html
It has properties:
#property (nonatomic, readwrite) float value;
#property (nonatomic, readwrite) minimumValue;
#property (nonatomic, readwrite) maximumValue;
#property (nonatomic, readwrite) int steps;
the recalculation:
- (void)recalcuateValue
{
float stepValues[self.steps];
stepValues[0] = self.minimumValue;
stepValues[self.steps - 1] = self.maximumValue;
for(int i = 1; i < self.steps; i++){
stepValues[i] = i * (self.maximumValue - self.minimumValue) / (self.steps - 1);
if (self.value < stepValues[i] && self.value > stepValues[i-1]){
self.value = (self.value > (stepValues[i] - stepValues[i-1]) / 2 + stepValues[i-1])?stepValues[i]:stepValues[i-1];
}
}
}
And the ccTouchesEnded: I added if(self.steps != 0), for the case steps is set to 0, the slider could work in usual mode
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
if ([self isSelected]){
self.value = [self valueForLocation:_thumbSprite.position];
if(self.steps != 0) {
[self recalcuateValue];
[_thumbSprite setPosition:[self locationFromValue:self.value]];
}
}
self.thumbSprite.color = ccWHITE;
self.selected = NO;
}
It calls valueForLocation and locationFromValue methods:
- (float)valueForLocation:(CGPoint)location
{
float percent = location.x / _backgroundSprite.contentSize.width;
return _minimumValue + percent * (_maximumValue - _minimumValue);
}
- (CGPoint)locationFromValue:(float)value{
float percent = self.value / self.maximumValue;
return ccp(percent * _backgroundSprite.contentSize.width, _backgroundSprite.position.y);
}
So an example of usage. I needed a slider with 3 steps and values 0, 1 and 2 on each step:
self.Slider = [CCControlSlider sliderWithBackgroundFile:#"sliderTrack.png"
progressFile:#"sliderProgress.png"
thumbFile:#"sliderThumb-hd.png"]; progressFile:#"sliderProgress.png" thumbFile:#"sliderThumb-hd.png"];
self.Slider.minimumValue = 0.0f;
self.Slider.maximumValue = 2.0f;
self.Slider.steps = 3;
self.Slider.value = [[GameSettings sharedSettings] defaultAILevel];
[self.Slider addTarget:self action:#selector(onSliderValueChanged:) forControlEvents:CCControlEventValueChanged];
Hope it may be useful
I created my discrete slider this way:
#import <UIKit/UIKit.h>
#interface DiscreteSlider : UISlider {
int step;
}
#property (nonatomic) int step;
#end
which that implementation:
#import "DiscreteSlider.h"
#implementation DiscreteSlider
#synthesize step;
- (void) recalcuateStep
{
float lValue = self.value;
int lLowerValue = (int) ( lValue / self.step );
float lDifference = lValue - ( lLowerValue * step );
float lHalfStep = ((float) step) / 2;
if( lDifference < lHalfStep ) {
} else {
self.value = (float) ( lLowerValue + step );
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
- (void) touchesMoved:(NSSet *) touches
withEvent:(UIEvent *) event
{
[super touchesMoved:touches withEvent:event];
NSLog( #"DS.touchesMoved(), event: %#", event );
[self recalcuateStep];
}
- (void) endTrackingWithTouch:(UITouch *) touch
withEvent:(UIEvent *) event
{
[super endTrackingWithTouch:touch withEvent:event];
NSLog( #"DS.endTrackingWithTouch(), event: %#", event );
[self recalcuateStep];
}
- (int) step {
return ( step ? step : 1 );
}
#end
Related
I've been having problems with xcode in general. Example, NSLog not printing, case statements not working, etc. I did created this project before xcode 6 came out and thought if i switched it to a new file made in xcode 6 that these things would have been fixed. So far, they have been fixed. However, more problems have happened. One major one is that the didBeginContact method is not being called now. I've tried doing everything to fix it. I changed the category masks, their values, the collisionBitMask, contactBitMask, put different foundations in, but nothing has worked yet. It works in the file made before xcode 6 came out, but not in xcode 6.
There is supposed to be contact detected between the player and the opponent, but it doesn't work anymore.
I put breakpoints at the didBeginContact method and at the method it is supposed to call when there is contact between the player and opponent, but program didn't exit.
Thanks in advance!
GameScene.m:
#import "GameScene.h"
#interface GameScene ()
#end
#implementation GameScene
- (void)didMoveToView:(SKView *)view
{
if (!self.contentCreated)
{
[self createSceneContents];
self.contentCreated = YES;
[self addplatform];
[self addPlayer];
[self addButtons];
[self addOpponent];
}
}
typedef NS_OPTIONS(uint32_t, CollisionCategory) {
CollisionCategoryPlayer = 1 << 0,
CollisionCategoryOpponent = 1 << 1,
CollisionCategoryPlatform = 1 << 2,
CollisionCategoryPlatformBorder = 1 << 3,
};
//static inline CGFloat skRandf() {
// return rand() / (CGFloat) RAND_MAX;
//}
- (void)createSceneContents
{
self.backgroundColor = [SKColor purpleColor];
self.scaleMode = SKSceneScaleModeAspectFit;
self.multipleTouchEnabled = YES;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
self.physicsWorld.contactDelegate = self;
}
return self;
}
- (SKSpriteNode *)addPlayer {
self._player = [SKSpriteNode spriteNodeWithTexture: [SKTexture textureWithImageNamed:#"TurnipSmall"]];
self._player.position = CGPointMake(100,450);
self._player.anchorPoint = CGPointMake(0.5,0.5);
self._player.physicsBody = [SKPhysicsBody bodyWithTexture:self._player.texture size:self._player.texture.size];
self._player.name = #"player";
self._player.physicsBody.dynamic = YES;
self._player.physicsBody.allowsRotation = FALSE;
self._player.physicsBody.affectedByGravity = TRUE;
self._player.physicsBody.friction = 5;
self._player.physicsBody.mass = 10;
self._player.physicsBody.usesPreciseCollisionDetection = YES;
self._player.physicsBody.restitution = 0.0;
self._player.physicsBody.categoryBitMask = CollisionCategoryPlayer;
self._player.physicsBody.collisionBitMask = CollisionCategoryPlayer | CollisionCategoryOpponent | CollisionCategoryPlatform;
self._player.physicsBody.contactTestBitMask = CollisionCategoryPlayer | CollisionCategoryOpponent | CollisionCategoryPlatform;
self.playerPunching = false;
[self addChild:self._player];
return self._player;
}
- (SKSpriteNode *)addOpponent {
self._opponent = [SKSpriteNode spriteNodeWithTexture: [SKTexture textureWithImageNamed:#"Tomato"]];
self._opponent.position = CGPointMake(300, 450);
self._opponent.anchorPoint = CGPointMake(0.5, 0.5);
self._opponent.physicsBody = [SKPhysicsBody bodyWithTexture:self._opponent.texture size:self._opponent.texture.size];
self._opponent.name = #"opponent";
self._opponent.physicsBody.dynamic = YES;
self._opponent.physicsBody.allowsRotation = NO;
self._opponent.physicsBody.affectedByGravity = YES;
self._opponent.physicsBody.friction = 5;
self._opponent.physicsBody.mass = 10;
self._opponent.physicsBody.density = 5;
self._opponent.physicsBody.usesPreciseCollisionDetection = YES;
self._opponent.physicsBody.restitution = 0;
self._opponent.physicsBody.velocity = CGVectorMake(0, 0);
self._opponent.physicsBody.categoryBitMask = CollisionCategoryOpponent;
self._opponent.physicsBody.collisionBitMask = CollisionCategoryPlatform| CollisionCategoryPlayer;
self._opponent.physicsBody.contactTestBitMask = CollisionCategoryPlayer | CollisionCategoryPlatform;
[self addChild:self._opponent];
return self._opponent;
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if ((firstBody.categoryBitMask & CollisionCategoryPlayer) != 0 && (secondBody.categoryBitMask & CollisionCategoryOpponent))
{
[self playerTouchingOpponent];
}
}
GameScene.h:
//
// GameScene.h
// TEST
//
// Copyright (c) 2014 G Hui. All rights reserved.
//
#import <SpriteKit/SpriteKit.h>
#interface GameScene : SKScene <SKPhysicsContactDelegate>
#property bool multipleTouchEnabled;
#property BOOL contentCreated;
#property SKSpriteNode * _donut;
#property SKSpriteNode * _player;
#property SKSpriteNode * _opponent;
#property SKSpriteNode *platform1Scene1;
#property BOOL movementBegins;
#property NSArray *level1;
#property BOOL playerPunching;
#property bool alreadyPunching;
#property float characterNumber;
#property SKSpriteNode *platform2Scene1;
#property SKSpriteNode *platform3Scene1;
#property float playerHealth;
#property SKSpriteNode *_healthBar;
#property SKSpriteNode *rightplatformBorder;
#property SKSpriteNode *mask;
#property SKNode *_playerHealthBar;
#property SKNode *_opponentHealthBar;
#property SKNode *_playerPowerUpBar;
#property SKNode *_opponentPowerUpBar;
#property int _playerHP;
#property int _opponentHP;
#property const int MaxHP;
#property int _playerPowerUp;
#property int _opponentPowerUp;
#property const int MaxPowerUp;
#property const float healthBarWidth;
#property const float healthBarHeight;
#property const float powerBarWidth;
#property const float powerBarHeight;
#property bool touchingPlatform;
#property SKSpriteNode *sideBorder;
#property SKSpriteNode *frontBorder;
#property BOOL playerOpponentContact;
#property float distanceBetweenPlayerAndOpponent;
#property float distanceBetweenOpponentAndPlayer;
#end
Fixed!
It was because the contact delegate was in initWithSize, which wasn't being called for some reason...
Trying to set an int value using a Segmented Controller. I've seen several tuts on how to change labels, but I need to set an int value.
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize caseCost;
#synthesize dilution;
#synthesize returnMsg;
#synthesize opcValue;
//synthesize opc; < -- Tried
//int opc; <--- tried
- (IBAction)opcView:(id)sender {
if (opcValue.selectedSegmentIndex == 0) {
int opc = 320;
}
if (opcValue.selectedSegmentIndex == 1) {
int opc = 128;
}
if (opcValue.selectedSegmentIndex == 2) {
int opc = 135;
}
if (opcValue.selectedSegmentIndex == 3) {
int opc = 88;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//int opc; <--- tried
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)finishBtn:(id)sender {
//int opc = 320;
float case_cost = ([caseCost.text floatValue]);
float dilutionValue = ([dilution.text floatValue]);
float gpc = (opc / dilutionValue);
float gCost = (case_cost / gpc);
float bCost = (gCost / 4);
float bpc = (gpc * 4);
NSNumberFormatter *formatterCur = [[NSNumberFormatter alloc] init];
NSNumberFormatter *formatterInt = [[NSNumberFormatter alloc] init];
[formatterCur setNumberStyle:NSNumberFormatterCurrencyStyle];
[formatterInt setNumberStyle:NSNumberFormatterDecimalStyle];
NSString *bottlesCost = [formatterCur stringFromNumber:[NSNumber numberWithFloat:bCost]];
NSString *gallons = [formatterInt stringFromNumber:[NSNumber numberWithInt:gpc]];
NSString *gallonsCost = [formatterCur stringFromNumber:[NSNumber numberWithFloat:gCost]];
NSString *bottles = [formatterInt stringFromNumber:[NSNumber numberWithInt:bpc]];
returnMsg.text = [NSString stringWithFormat:#"%# gallons per case at %# per gallon and %# - 32 oz bottles at %# per bottle.", gallons, gallonsCost, bottles, bottlesCost];
}
- (IBAction)opcView:(id)sender {
}
#end
in the line "float gpc = (opc / dilutionValue);" is shows as an unknown value of opc, even though I think it should from the segmented controller. I'm using the segmented controller instead of Radio Buttons i've used in Java. I used the "//int opc=320" to make sure the rest of the code worked.
In each of the if blocks in your method - (IBAction)opcView:(id)sender you are creating a local int variable named opc. So when execution leaves the if block, the local variable disappears. Thus, in - (IBAction)finishBtn:(id)sender there is no variable named opc in scope.
You should declare opc to be a property as well. You will set this property when the segment control changes selection. Later, you can read the property's value in your finish button's handler.
#import "SecondViewController.h"
#interface SecondViewController()
#property (nonatomic) int opc;
#end
#implementation SecondViewController
// this method is wired to the segment control's UIControlEventValueChanged event
- (IBAction)opcView:(id)sender
{
if (opcValue.selectedSegmentIndex == 0) {
self.opc = 320;
}
if (opcValue.selectedSegmentIndex == 1) {
self.opc = 128;
}
if (opcValue.selectedSegmentIndex == 2) {
self.opc = 135;
}
if (opcValue.selectedSegmentIndex == 3) {
self.opc = 88;
}
}
- (IBAction)finishBtn:(id)sender
{
float case_cost = ([caseCost.text floatValue]);
float dilutionValue = ([dilution.text floatValue]);
float gpc = (self.opc / dilutionValue);
// lots more code
}
I want to cut a sprite in 6 equivalent parts just with one image, a .png file which I found on the web, no with texturepacker, (the image below by example)
I can take other way, but I want to know if I can do that. any one haves idea?
I'll revise my answer if this isn't what you were asking about, but I think you are asking how to 'manually run an animation' using a spritesheet without a plist.
Here's one way. It would be better if you encapsulated this into it's own class, but this can push you in the right direction I think:
ManualAnimationTest.h
#interface ManualAnimationTest : CCLayer
{
CCSprite *animatedSprite;
int x,y;
float animatedSpriteWidth, animatedSpriteHeight;
int animatedSpriteColumns, animatedSpriteRows;
}
#end
ManualAnimationTest.m
#import "ManualAnimationTest.h"
#implementation ManualAnimationTest
-(id) init
{
if( (self=[super init]))
{
CGSize s = [CCDirector sharedDirector].winSize;
x = 0;
y = 0;
animatedSpriteColumns = 3;
animatedSpriteRows = 2;
animatedSpriteWidth = 95.0f;
animatedSpriteHeight = 125.0f;
animatedSprite = [CCSprite spriteWithFile:#"animal_animation.png" rect:CGRectMake(x * animatedSpriteWidth,y * animatedSpriteHeight,animatedSpriteWidth,animatedSpriteHeight)];
[self addChild:animatedSprite];
[animatedSprite setPosition:ccp(s.width / 2.0f, s.height / 2.0f)];
[self schedule:#selector(animateAnimatedSprite) interval:0.5f];
}
return self;
}
-(void) animateAnimatedSprite
{
[animatedSprite setTextureRect:CGRectMake(x * animatedSpriteWidth, y * animatedSpriteHeight, animatedSpriteWidth, animatedSpriteHeight)];
x +=1;
if(x > (animatedSpriteColumns - 1))
{
x = 0;
y +=1;
}
if(y > (animatedSpriteRows - 1))
{
y = 0;
}
}
#end
How can I test if the scroll view is bouncing? Is there a notification or something when the bounce ends?
Here is how I detected if the scroll view is bouncing horizontally:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.x < 0) {
NSLog(#"bouncing left");
}
if (scrollView.contentOffset.x > (scrollView.contentSize.width - scrollView.frame.size.width)) {
NSLog(#"bouncing right");
}
}
I've implemented extension for UIScrollView to handle this for vertical and horizontal scrolling. This will work even with non-zero contentInsets and in case when content is not big enough for covering scrollView insets:
Objective-C
#interface UIScrollView (Bouncing)
#property (nonatomic, readonly) BOOL isBouncing;
#property (nonatomic, readonly) BOOL isBouncingTop;
#property (nonatomic, readonly) BOOL isBouncingLeft;
#property (nonatomic, readonly) BOOL isBouncingBottom;
#property (nonatomic, readonly) BOOL isBouncingRight;
#end
#implementation UIScrollView (Bouncing)
- (BOOL)isBouncing
{
return self.isBouncingTop || self.isBouncingLeft || self.isBouncingBottom || self.isBouncingRight;
}
- (BOOL)isBouncingTop
{
return self.contentOffset.y < - self.contentInset.top;
}
- (BOOL)isBouncingLeft
{
return self.contentOffset.x < - self.contentInset.left;
}
- (BOOL)isBouncingBottom
{
BOOL contentFillsScrollEdges = self.contentSize.height + self.contentInset.top + self.contentInset.bottom >= CGRectGetHeight(self.bounds);
return contentFillsScrollEdges && self.contentOffset.y > self.contentSize.height - CGRectGetHeight(self.bounds) + self.contentInset.bottom;
}
- (BOOL)isBouncingRight
{
BOOL contentFillsScrollEdges = self.contentSize.width + self.contentInset.left + self.contentInset.right >= CGRectGetWidth(self.bounds);
return contentFillsScrollEdges && self.contentOffset.x > self.contentSize.width - CGRectGetWidth(self.bounds) + self.contentInset.right;
}
#end
Swift 3.0+
extension UIScrollView {
var isBouncing: Bool {
return isBouncingTop || isBouncingLeft || isBouncingBottom || isBouncingRight
}
var isBouncingTop: Bool {
return contentOffset.y < -contentInset.top
}
var isBouncingLeft: Bool {
return contentOffset.x < -contentInset.left
}
var isBouncingBottom: Bool {
let contentFillsScrollEdges = contentSize.height + contentInset.top + contentInset.bottom >= bounds.height
return contentFillsScrollEdges && contentOffset.y > contentSize.height - bounds.height + contentInset.bottom
}
var isBouncingRight: Bool {
let contentFillsScrollEdges = contentSize.width + contentInset.left + contentInset.right >= bounds.width
return contentFillsScrollEdges && contentOffset.x > contentSize.width - bounds.width + contentInset.right
}
}
For RxSwift you can just map scrollViewDidScroll with isBouncing and filter with distinctUntilChanged.
A minor amendment to Justin's method, allowing for contentInset:
if( scrollView.contentOffset.x < -scrollView.contentInset.left )
{
NSLog( #"bounce left" );
}
if( scrollView.contentOffset.x > scrollView.contentSize.width - scrollView.frame.size.width + scrollView.contentInset.right )
{
NSLog( #"bounce right" );
}
Yes... check out the UIScrollViewDelegate spec, implement the methods including the two below, and set your UIScrollView's delegate accordingly:
// User stops dragging the table view.
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate;
// Control slows to a halt after the user drags it
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
You will probably be most interested in scrollViewDidEndDecelerating. These also work in UITableView which is where I originally found them (UITableView inherits from UIScrollView).
Old question but I just ran into a similar issue and wanted to add that it is a good idea to also check that the scroll views content is larger than the scroll view's frame:
+ (BOOL) isScrollViewBouncing:(UIScrollView *)scrollView
{
return scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.frame.size.height
&& scrollView.contentSize.height > scrollView.frame.size.height;
}
This now makes sure the scroll view is large enough to be in a scroll-bouncing state, so that if the scroll view is small it does NOT always evaluate to true.
Cheers
For those of you who would be able to "bounce" downwards in a scrollview in order to update contents of the view. (And the event is only fired once.)
- (void)scrollViewDidEndDragging:(UIScrollView *)aScrollView
willDecelerate:(BOOL)decelerate{
CGPoint offset = aScrollView.contentOffset;
CGRect bounds = aScrollView.bounds;
CGSize size = aScrollView.contentSize;
UIEdgeInsets inset = aScrollView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
//Distance in points before update-event occur
float reload_distance = 50;
//
if(y > h + reload_distance) {
NSLog(#"load more rows");
}
}
Using Glavid's answer, I added bouncing to bottom also, and added as a category
#import "UIScrollView+Additions.h"
#implementation UIScrollView (Additions)
- (BOOL)isBouncing
{
BOOL isBouncingBottom = self.contentOffset.y >= self.contentSize.height - self.frame.size.height
&& self.contentSize.height >= self.frame.size.height;
BOOL isBouncingTop = self.contentOffset.y <= 0;
BOOL isBouncing = isBouncingBottom || isBouncingTop;
return isBouncing;
}
#end
i have 10 moving objects (UIImageView),
is there a better way to write this code?
- (void) jumpOnTimer {
jumpBall1.center = CGPointMake(jumpBall1.center.x+pos1.x,jumpBall1.center.y+pos1.y);
if(jumpBall1.center.x > 60 || jumpBall1.center.x < 0)
pos1.x = -pos1.x;
if(jumpBall1.center.y > 211 || jumpBall1.center.y < 82)
pos1.y = -pos1.y;
jumpBall2.center = CGPointMake(jumpBall2.center.x+pos2.x,jumpBall2.center.y+pos2.y);
if(jumpBall2.center.x > 40 || jumpBall2.center.x < 0)
pos2.x = -pos2.x;
if(jumpBall2.center.y > 206 || jumpBall2.center.y < 82)
pos2.y = -pos2.y;
and so on...
Judging by that code snippet, it looks like you have a single controller which "owns" the ten balls, and you want the balls to bounce around according to a set of rules that are unique to each ball. A more object-oriented approach would be as follows:
#interface JumpBallClass
{
CGPoint center;
CGPoint speed;
CGPoint lowerLimit;
CGPoint upperLimit;
}
#property (assign) CGPoint lowerLimit;
#property (assign) CGPoint upperLimit;
- (void)update;
#end
#implementation JumpBallClass
- (void)update
{
center.x += speed.x;
center.y += speed.y;
if (center.x > upperLimit.x || center.x < lowerLimit.x)
{ speed.x = -speed.x; }
if (center.y > upperLimit.y || center.y < lowerLimit.y)
{ speed.y = -speed.y; }
}
#end
This setup would allow you to configure all of the balls once, by setting their upper and lower limits:
[jumpBall1 setUpperLimit:CGPointMake(60, 211)];
[jumpBall1 setLowerLimit:CGPointMake(0, 82)];
...
And then simply calling update on each ball in your timer method:
- (void) jumpOnTimer {
[jumpBall1 update];
[jumpBall2 update];
...
}
You can simplify this even further by storing all of the balls in an NSArray:
NSArray * balls = [NSArray arrayWithObjects:jumpBall1, jumpBall2, ..., nil];
And then calling makeObjectsPerformSelector:
[balls makeObjectsPerformSelector:#selector(update)];
You can make an array of jumpBalls and then loop through each and do the code for that. You can do something like this:
JumpBallClass *myjumpballs[10];
for (i=0; i<10; i++) {
myjumpballs[i].center = CGPointMake(myjumpballs[i].center.x+pos1.x,myjumpballs[i].center.y+pos1.y);
if(myjumpballs[i].center.x > 60 || myjumpballs[i].center.x < 0)
pos1.x = -pos1.x;
if(myjumpballs[i].center.y > 211 || myjumpballs[i].center.y < 82)
pos1.y = -pos1.y;
}
Looks like you're trying to manually animate. Have a look at using UIView animations instead