Here are a few of the things that I have learned.
Some of these things I learned the hard way(tm)(t).
Other things, were told to me by really cool people. My mom and dad, friends, my bosses, podcasts, books, magazines, celebrities....
The odd thing about this, is sometimes I was told one of these things, but I still had to learn it the hard way(tm)(r).
Still others were learned by simple observation.
I'm going to try and trim this list down, and create posts for each item. Where an item has some subtext, that is the start of a new post. I hope you read this list and get something out of it.
Richs' House of Blues and Vodka
Rich is a software developer with over 25 years of software development and project management experience.
Friday, March 9, 2018
Thursday, March 8, 2018
Rich's Rule of Life # 1 - If, at your place of work, you cannot identify "side show knob", then you are that person.
There is always that guy, or gal at work.
The one that annoys everyone.
The one that jumps up from their desk the second your arrive at work, before you can make your goddamn coffee, and comes running at you with some stupid problem about the stupid customer that has no bearing on the import....
Oh, sorry. Let me take a sip of coffee..
No, its the one who just wants to remind you that you haven't filled out your United Way card yet, and that "there are really easy ways to donate a percentage of your income to this wonderful charity" that's an umbrella charity for all of the charities that you hate, but don't worry they will only send your money to the charities you like, just fill out the form with the amount you want give... "Oh, you don't want to give? Why not? If you don't like some of the charities then your donation will be colour coded to only go to the ones you like.. Oh, you've set up your own donations? Are you sure they are doing the important work that the United Way is doing?? And it really looks great for the company when it's employees give so much money!" !@$@@$% LEAVE ME ALONE I WANT TO DO WORK!!!
sip of coffee.. ok, I'm better...
Ok. Or maybe it's that person with model airplanes all over his desk on nice little stands, all perfectly painted with the real liveries of the actual airplane, or the actual load outs of the attack plane, who grabs your client to show them the actual little man sitting in the canopy of the goddamn YF-22 Advanced Tactical Fighter, and how he's fucking anatomically correct, with all the actual goddamn colour of the goddamn uniform...
Ok, one more sip...
Or maybe, just maybe it's that developer that hangs out at the receptionists desk and offers to bring her a coffee, oh, she doesn't like coffee? What about a tea? He can go to the tea shop around the corner... oh, ok, what about a coke? @#$@% LEAVE THE POOR WOMAN ALONE AND FIX THE F'ING BUG I ASKED YOU TO FIX ALREADY!!!
Sigh... another sip..
Or maybe, just maybe, all of your co-workers are awesome. No one is pissing you off... But, you notice that people seem hesitant when you walk by...
The one that annoys everyone.
The one that jumps up from their desk the second your arrive at work, before you can make your goddamn coffee, and comes running at you with some stupid problem about the stupid customer that has no bearing on the import....
Oh, sorry. Let me take a sip of coffee..
No, its the one who just wants to remind you that you haven't filled out your United Way card yet, and that "there are really easy ways to donate a percentage of your income to this wonderful charity" that's an umbrella charity for all of the charities that you hate, but don't worry they will only send your money to the charities you like, just fill out the form with the amount you want give... "Oh, you don't want to give? Why not? If you don't like some of the charities then your donation will be colour coded to only go to the ones you like.. Oh, you've set up your own donations? Are you sure they are doing the important work that the United Way is doing?? And it really looks great for the company when it's employees give so much money!" !@$@@$% LEAVE ME ALONE I WANT TO DO WORK!!!
sip of coffee.. ok, I'm better...
Ok. Or maybe it's that person with model airplanes all over his desk on nice little stands, all perfectly painted with the real liveries of the actual airplane, or the actual load outs of the attack plane, who grabs your client to show them the actual little man sitting in the canopy of the goddamn YF-22 Advanced Tactical Fighter, and how he's fucking anatomically correct, with all the actual goddamn colour of the goddamn uniform...
Ok, one more sip...
Or maybe, just maybe it's that developer that hangs out at the receptionists desk and offers to bring her a coffee, oh, she doesn't like coffee? What about a tea? He can go to the tea shop around the corner... oh, ok, what about a coke? @#$@% LEAVE THE POOR WOMAN ALONE AND FIX THE F'ING BUG I ASKED YOU TO FIX ALREADY!!!
Sigh... another sip..
Or maybe, just maybe, all of your co-workers are awesome. No one is pissing you off... But, you notice that people seem hesitant when you walk by...
Rich's Rule of Life #0 - If the worst thing that happens to you today is that you've lost your job, you're doing OK.
This rule was given to me by the vice president at the first post-university job I held, it took me many years to realize the truth of it. For a long time, I felt that my job was my life and that if you failed at your job you failed at life...
After a quitting, or being laid off a few times, I realized that, in general, the next job was better. I was able to identify the types of jobs I really wanted and sought them out actively. My job hunting skills became much better with practice... Lot's of practice.. IMMENSE amounts of practice.
I had sent out so many cover letters and resumes that I was excited whenever I got a rejection letter! I had been keeping a really great database of my job-hunting activities and I was able to correlate rejections to job-hunting actions. When I saw that something wasn't working, I changed things up a small bit and kept moving on.
I learned very early to go to interviews with applications that I had written and always gave a small presentation to those who were there. I judged their reactions to my presentation and decided if *I* wanted to work with them.
Interviews suddenly became me interviewing the employers, not the employers interviewing me. I realized that I had a bit of control, I could say "no".
I no longer worry about losing my job. I will always find a new, better one.
After a quitting, or being laid off a few times, I realized that, in general, the next job was better. I was able to identify the types of jobs I really wanted and sought them out actively. My job hunting skills became much better with practice... Lot's of practice.. IMMENSE amounts of practice.
I had sent out so many cover letters and resumes that I was excited whenever I got a rejection letter! I had been keeping a really great database of my job-hunting activities and I was able to correlate rejections to job-hunting actions. When I saw that something wasn't working, I changed things up a small bit and kept moving on.
I learned very early to go to interviews with applications that I had written and always gave a small presentation to those who were there. I judged their reactions to my presentation and decided if *I* wanted to work with them.
Interviews suddenly became me interviewing the employers, not the employers interviewing me. I realized that I had a bit of control, I could say "no".
I no longer worry about losing my job. I will always find a new, better one.
Thursday, May 19, 2016
Glyn Barrett Tells his Congregation about the Debate he Wished had Happened!
Glyn Barrett Lead Pastor of !Audacious Church |
I'll leave a number of links at the end of this post where you can get the whole story.
But here is Pastor Glyn Barretts triumphant version of his story, transcribed from a video, who's link will be produced at the end of this blog:
A little while ago I was invited to a University to speak at the Atheistic Society.When, Glyn? What was the date? And the "Atheistic Society"? Really? Check the links at the end of this post for the full story.
They said: "Pastor Barrett we would like you come and get involved in a debate with all the atheists.All the atheists? At once? Seriously?
I said: "Fine."You found a group of university atheists that make this positive claim? None of them had any philosophy training then.. OK. In general, most atheists won't claim that as it is not possible to prove or disprove the existence of leprechauns, or gods, or Glyn Barrett's god.
I said: "What's the heading, whu, what's, what's the caption, what're we debating on?"
And they said this: "The debate is this, the premise is 'There is no Gawwwd'"
So, OK. That together the president of the "Atheistic Club", he will stand up, and he will share for fifteen minutes on how there is no god, and the we're going to give you ten minutes to prove to us that there is a God.Wait. Now it's just the "lead" atheist? Not all the atheists? Why didn't you call them on this and mock them for changing the rules at the last second? Oh wait, it's your fantasy... Fair enough.
Anyway, here we have Glyn Barrett espousing xtian persecution porn: the evil atheists will take fifteen minutes to make their point while the poor persecuted christian debater only gets ten minutes.
Here's a bit of advice, Mr. Barrett, don't accept debate terms like this, it shows that the people who have set up the debate are, well, dishonest... Of course, since this never happened...
I said, "Bro, I don't need ten minutes, I only need two minutes"Aaaand here comes Glyn Barrett, the Christian warrior hero. Taking on the evil atheists at a great disadvantage...
He said: "Come on down"You won't tell us, and then you tell us...
So I went down, I won't tell you which university it was, but I, I drove to Cambridge. And when I got to Cambridge(audience laughs). I go into this lecture hall, right, and there's, I don't know, a thousand, eight hundred, thousand students there.
Cambridge, eh? Very prestigious! You took on the "Atheistic Society" at a very prestigious University. And, you were the only christian in a lecture hall of eight hundred to a thousand evil atheists... Glyn Barrett sees himself as the noble Christian being thrown to the evil atheistic lions.
In general, debates are attended by people on both sides of the issue. In fact, you should have invited your church members to attend. But, since, Glyn Barrett, this whole debate is fictional, that would have been impractical.
Aaaa, and the lead atheist he gets up and he's preaching, and he's spitting, he's frothing at the mouth, and he's eloquent, he's brilliant: "How can any rational person believe that there is a God. You gotta be stupid to believe that there is a God" , all of these sorts of things, sorts of things that you've heard.So, to you, Glyn Barrett, "preaching..spitting...frothing" is eloquent and brilliant?
Are you sure that's what the "lead atheist" said? (Of course not, it's a figment of your imagination) Because that's not an argument for or against the stated "premise" of this debate. Your debate opponent lost right there, you could have gone home.
And then I said: "And now we're gonna get Glyn Barrett and he's gonna come up and tell us why there is a god."Why would you have to do your own introduction? Oh well, In general, at real debates, introductions occur at the beginning of the debate. They are generally polite, and do a really good job of telling the audience about the background of the participants in the most positive light.
Have you ever read the verse led like a lamb to the slaughter?(audience laughs)I presume, Glyn Barrett, you are foreshadowing the slaughter of the atheists here, and not leading your self to your embarrassment on the internet. So badly embarrassed that you had to remove the video of your triumphant debate from the internet..
So I get up on stage, and there's boos and hisses and all these sorts of things and, and I come up the front and I just said to the guy, I said: "Listen, ah, you spoke so eloquently",Boos and hisses? Really? You should have walked away at that point. That seems like a very dangerous position for you to be in. But it never happened. Ah, Glyn Barrett, you really need to get over your love of Christian persecution porn. And, in a real debate, you'd already be on the stage.
I said, "Come and join me on stage."Again, your opponent would already be on stage. Please, Glyn Barrett, at least review actual debates before you make up your stories about them.
So he came 'n joined me at the podium at the front there, 'n, and I said, "You believe there is no God". He said, "There is no God.. You cannot be a rational person and believe there is a God".Dubious, Glyn Barrett. We really need to see the video of the debate (which doesn't exist, of course), that is not a position that a philosophically minded Cambridge university atheist would take.
I said, "OK, help me out with this, I wanna show you something", and I drew a circle on a whiteboard like this (draws circle on a pad of paper for his audience)...We all know what's coming. This has been done so many times. Glyn Barrett is simply "borrowing" this from Ray "Banana Man" Comfort, Josh Feuerstein and others(see the links at the end of this post). It's called the God of the Gaps fallacy.
Here's a small hint, Glyn Barrett, it's a fallacy not an argument. But, Glyn Barrett, by all means, continue. (Napoleon had something to say about this: Never interrupt your enemy when he is making a mistake.)
Glyn Barrett launches the God of the Gaps Fallacy!
I said, "This circle represents knowledge. Everything that can ever be know about any subject fits in there. All the sciences, all the arts. You're an intelligent man, you study at this university. How much do you know of everything that could possible be known?"
So he took a pen and he drew that (draws a secant chord line at the top of the circle taking up about 5% of the area of the circle) and he went: "I reckon I know about this much."Well, that's pretty arrogant of the atheist leader here, isn't it? I can't help but to think that Glyn Barrett is projecting a little bit here.
I said, "Wow, you're intelligent!""Dimension"? Perhaps area would have been a better choice, but OK. So, in the bit that you haven't discovered, Glyn Barrett, could the Hindu gods exist? How about Allah? Or any of the other gods that you don't believe in?
He said, "Yeah, I am"
I said "Brilliant!"
I said "So that's what you *do* know!"
He said "Yes".
I said, "And that's what you don't know".
He said "Yes".
"But this does exist, you just haven't discovered it yet"
"Yes"
"Therefore, could God not exist in the dimension you haven't yet discovered."
(Audience erupts in applause)
Answer: "Yes"Why yes, Glyn Barrett, all of those gods and other mystical characters could exist in the "Dimension" that you haven't discovered.
"Therefore, sir, I would suggest you're not an atheist, you're an agnostic, and one step closer to knowing my Jesus."And by that logic, Glyn Barrett, you should be one step closer to knowing everyone else's mystical beings.
Atheists can be agnostic. If you ask an atheist agnostic what god she believes in, she won't be able to name one. She doesn't have a belief in a god or gods, AND being agnostic she will make the claim that she can't know.
You see, Glyn Barrett, agnostic doesn't mean what you think it means; an agnostic is someone who believes that it is impossible to know anything about gods.
So your claim that the leader of the "Atheistic Club" is one step closer to Jesus is just inane. Why wouldn't place the person one step closer to Thor?
Here's a fun thing I noticed: while transcribing Glyn Barrett's story, I played it back at half speed, try it, it makes him sound really drunk, which could be a really good explanation for the story.
Here are the links that I promised:
- Glyn Barrett's sermon that started it all, thanks to Hemant Mehta, since Glyn Barrett and the !Audacuious Church took the original sermon down, for some reason....
- A website that exposes Glyn Barrett for the liar he is: !Audacious Lies
- Seth Andrews, The Thinking Atheist, has a great video called the "Debate that Wasn't There" about Glyn Barrett's debate "story".
- Hemant Mehta, The Friendly Atheist, first tackled Glyn Barrett's dishonesty with his blog post "Christian Pastor Tells Dubious Story of Defeating Atheist in a Debate in Under Two Minutes", then followed up with "Seth Andrews Tackles the Dubious Story of a Pastor Who Supposedly Outwitted Atheists in a Debate" and then "Pastor Glyn Barrett’s Story of Winning a Fake Debate Against Atheists Isn’t Even Original"
- Ed Brayton, of Dispatches from the Culture Wars, had this take "Pastor Makes Up Story of How he Vanquished the Atheists"
- Luciano Gonzalez, of the Sin/God blog: "Then There’s This Guy."
- There's a petition trying to drum up support for a real debate at Cambridge: "Petition for Glyn Barrett to Take his Argument to Cambridge and Document the Outcome"
- Here's a post on the Skeptics Stack Exchange: "Was Glyn Barrett invited to speak at a meeting for an atheist society in Cambridge?". It turns out neither of the universities in the Cambridge area even knew who Glyn Barrett was, let alone invited him for a debate. And neither university has a group called the "Atheistic Society".
- Here's a discussion about the !Audacious Church on the Cult Education Forum
- Great analysis by the youtuber Xplore with James Peter:"Glyn Barrett Defeats Atheist in Debate - Audacious Church"
This story is alive. I suspect I'll keep adding to this list of links.
Wednesday, November 11, 2015
My Experiments with SpriteKit: Part Five - Faking Inertia!
Hopefully, you had accepted my challenge to attempt to move the spaceship by adding the code hints that I gave you in Part Four. If so, you may have added something like this to the switch statement in the update: method
Unfortunately, we can now "fly" our spaceship off the GameScene, and it's very hard to get back.. Let's make our GameScene a "closed universe", that is, if the spaceship flies off the scene, it will reappear at the opposite wall. This can be achieved with a few if statements in the update: method.
First, since our SKSpriteNode does not allow modifying its' position in place, we need to get a copy of it, then we compare the position of the spaceship to the bounds of the game scene:
There! Now we won't lose our spaceship.
Uh-oh! You may have noticed that since we have added a third keypress, the up key, we seem to have broken our left and right rotation functionality. But not too badly. Because of a keyboard problem called ghosting we do not have the ability to press the up, left and right buttons at the same time and get all of the keyDown events... Oh, well. Our users will adjust.
Now, on to the motion. Our spaceship doesn't move like a spaceship, it's more like a car. Let's give it a bit of "momentum". (We're not doing sprite physics yet, I promise I'll do that later, we're going to fake it now.)
In space, no one can hear you... ah.. slow down... Let's change the forward motion. Instead of using the up arrow key to directly move the spaceship, we'll use it to change the speed of the sprite. This looks like a job for a property! Since we're playing in a 2 dimensional space we should use a CGVector to represent the velocity of our spaceship. We can add that to the GameScene interface block
So now we have spaceship motion that simulates momentum, at least in forward motion, we could make the rotation behave the same way as well, that is, if the spaceship rotation is started it keeps rotating until a counter rotation is started. But before we make any more modifications to the spaceship we should do some refactoring first.
GameScene is getting a bit large, lets refactor it by extracting the spaceship into it's own class based SKSpriteNode... In the next blog post.
where c is a CGFloat, possibly a const, with a value of M_PI/2.0, that rotates the direction of motion to be inline with the way our drawing of the spaceship is pointing (we should probably fix the drawing code so that we can remove that hack). And that was it!case 126:{const CGFloat speed = 3.0;const CGFloat c = M_PI/2.0;CGPoint pt = _spaceship.position;pt.x += speed * cos(_spaceship.zRotation + c);pt.y += speed * sin(_spaceship.zRotation + c);_spaceship.position = pt;}break;
Unfortunately, we can now "fly" our spaceship off the GameScene, and it's very hard to get back.. Let's make our GameScene a "closed universe", that is, if the spaceship flies off the scene, it will reappear at the opposite wall. This can be achieved with a few if statements in the update: method.
First, since our SKSpriteNode does not allow modifying its' position in place, we need to get a copy of it, then we compare the position of the spaceship to the bounds of the game scene:
finally, we set the new value of the spaceship. Make sure to do this after the switch statement in the update: method.CGPoint pt = self.spaceship.position;if (pt.x>self.frame.size.width ) {pt.x = 0;} else if(pt.x < 0) {pt.x = self.frame.size.width;}if (pt.y>self.frame.size.height) {pt.y = 0.0;} else if(pt.y < 0) {pt.y = self.frame.size.height;}self.spaceship.position = pt;
There! Now we won't lose our spaceship.
Uh-oh! You may have noticed that since we have added a third keypress, the up key, we seem to have broken our left and right rotation functionality. But not too badly. Because of a keyboard problem called ghosting we do not have the ability to press the up, left and right buttons at the same time and get all of the keyDown events... Oh, well. Our users will adjust.
Now, on to the motion. Our spaceship doesn't move like a spaceship, it's more like a car. Let's give it a bit of "momentum". (We're not doing sprite physics yet, I promise I'll do that later, we're going to fake it now.)
In space, no one can hear you... ah.. slow down... Let's change the forward motion. Instead of using the up arrow key to directly move the spaceship, we'll use it to change the speed of the sprite. This looks like a job for a property! Since we're playing in a 2 dimensional space we should use a CGVector to represent the velocity of our spaceship. We can add that to the GameScene interface block
as a property called spaceshipVelocity. Now we need to set our velocity, in the switch statement we used velocity to set the position directly. But now we need to apply an acceleration to the velocity. So let's rewrite the up key handling code in the update: method@interface GameScene ()@property SKSpriteNode* spaceship;@property CGVector spaceshipVelocity;@property CGPoint center;@property CGFloat height;@property NSMutableSet* keysPressed;@end
Now that we have set the spaceship velocity we need to apply the velocity to the spaceship position. We currently grab the position of the spaceship just before we make sure that the spaceship is trapped in our view. Insert the position update code just after that pointcase 126:{const CGFloat accel = 0.1;CGVector v = self.spaceshipVelocity;v.dx += accel * cos(self.spaceship.zRotation + c);v.dy += accel * sin(self.spaceship.zRotation + c);self.spaceshipVelocity = v;}break;
we still have a small problem. Go ahead and run the application, you'll find that our spaceship can accelerate to a stupid large velocity. Let's limit the velocity before we apply it to the position, and only when we change the velocity, in the up arrow handlerCGPoint pt = self.spaceship.position;pt.x += self.spaceshipVelocity.dx;pt.y += self.spaceshipVelocity.dy;_spaceship.position = pt;if (pt.x>self.frame.size.width ) {pt.x = 0;} else if(pt.x < 0) {pt.x = self.frame.size.width;}if (pt.y>self.frame.size.height) {pt.y = 0.0;} else if(pt.y < 0) {pt.y = self.frame.size.height;}self.spaceship.position = pt;
here we limit the speed of our spaceship to 5 points per frame update. That seems to make a reasonable spaceship speed. We use the C function hypot function to calculate the speed from the velocity. Note that when we see that the speed would be too high, we renormalize the velocity back to the maximum speed before applying it to the spaceship.case 126:{const CGFloat accel = 0.1;const CGFloat maxSpeed = 5.0;CGVector v = self.spaceshipVelocity;v.dx += accel * cos(self.spaceship.zRotation + c);v.dy += accel * sin(self.spaceship.zRotation + c);CGFloat speed = hypot(v.dx, v.dy);if (speed>maxSpeed) {v.dx = (v.dx/speed) * maxSpeed;v.dy = (v.dy/speed) * maxSpeed;}self.spaceshipVelocity = v;}break;
So now we have spaceship motion that simulates momentum, at least in forward motion, we could make the rotation behave the same way as well, that is, if the spaceship rotation is started it keeps rotating until a counter rotation is started. But before we make any more modifications to the spaceship we should do some refactoring first.
GameScene is getting a bit large, lets refactor it by extracting the spaceship into it's own class based SKSpriteNode... In the next blog post.
Labels:
acceleration,
CGFloat,
CGPoint,
CGVector,
hypot,
SKSpriteNode,
velocity
Sunday, November 8, 2015
My Experiments with SpriteKit: Part Four - Allowing the User to Rotate The Sprite!
If you've been following along with my SpriteKit experiments you should have a cool retro spaceship in a bordered GameScene, just like this:
My implementation of GameScene looks like this:
#import "GameScene.h"@interface GameScene ()@property SKSpriteNode* spaceship;@property CGPoint center;@property CGFloat height;@end@implementation GameScene-(void)addBorderOfThickness:(CGFloat)t andCornerRadius:(CGFloat)cr {SKSpriteNode* border;NSImage* image;image = [[NSImage alloc] initWithSize:self.frame.size];[image lockFocus];NSBezierPath* path = [NSBezierPath bezierPathWithRect:self.frame];[path appendBezierPathWithRoundedRect:CGRectInset(self.frame, t, t)xRadius:cryRadius:cr];[[NSColor colorWithRed:(255.0/255.0)green:(215.0/255.0)blue:(0.0/255.0)alpha:1.0] set];path.windingRule = NSEvenOddWindingRule;[path fill];[image unlockFocus];SKTexture* texture = [SKTexture textureWithImage:image];border = [SKSpriteNode spriteNodeWithTexture:texture];border.position = self.center;[self addChild:border];}-(void)addSpaceship {NSImage* image;image = [[NSImage alloc] initWithSize:CGSizeMake(30, 30)];[image lockFocus];NSBezierPath* path = [NSBezierPath bezierPath];path.lineWidth = 2.5;[path moveToPoint:CGPointMake(0, 0)];[path lineToPoint:CGPointMake(15, 30)];[path lineToPoint:CGPointMake(30, 0)];[path lineToPoint:CGPointMake(15, 10)];[path closePath];[[NSColor colorWithRed:(255.0/255.0)green:(215.0/255.0)blue:(0.0/255.0)alpha:1.0] set];[path stroke];[image unlockFocus];SKTexture* texture = [SKTexture textureWithImage:image];_spaceship = [SKSpriteNode spriteNodeWithTexture:texture];_spaceship.position = self.center;[self addChild:_spaceship];}-(void)didMoveToView:(SKView *)view {self.backgroundColor = [NSColor blackColor];_height = CGRectGetHeight(self.frame);_center = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));[self addBorderOfThickness:15andCornerRadius:25];[self addSpaceship];}@end
Note that I've add a property to hold the spaceship sprite, now we can move it around in the update: method. Let's just rotate the spaceship one degree each update, for now. SKSpriteNode has a handy property called zRotation of type CGFloat.
This rotates our spaceship counter-clockwise around its' centre (the anchor point) at a rate of one degree per update. How would we rotate it clockwise? That's pretty easy, try it out.-(void) update:(NSTimeInterval)currentTime {const CGFloat oneDegree = 2.0 * M_PI / 360.0;self.spaceship.zRotation += oneDegree;}
Let's take a small detour to talk about the anchor point of our spaceship. In the debugger put a break point on a line inside of the update: method. In the (lldb) window of the debug area have a look at the anchorPoint property of our spaceship sprite:
anchorPoint is a property of type CGPoint. The ranges of the x and y values are 0.0 to 1.0. The default position of the anchorPoint for the sprite is x=0.5 and y=0.5, also known as the centre of the sprite. In the didMoveToView: method, after the call to the addSpaceship method change the anchorPoint property to (0.0, 0.0), like this:(lldb) po self.spaceship.anchorPoint(x = 0.5, y = 0.5)(x = 0.5, y = 0.5)(lldb)
and re run the application, you will now see the spaceship rotate around the lower left part of the spaceship, or the (0.0, 0.0) point of the sprite. If you set the anchor to ( 1.0, 1.0), the sprite will rotate around the upper right of the spaceship sprite. At this point I think we want to rotate the sprite around its' centre, so remove the line that sets the anchorPoint.self.spaceship.anchorPoint = CGPointMake(0.0, 0.0);
OK, we have a spinning spaceship, but that's not the goal. I would like to let our user use the left arrow key(ASCII Code: 123) to rotate the sprite count-clockwise and the right arrow key(ASCII Code: 124) to rotate the sprite clockwise. NSResponder, gives our SKSpriteNode a couple of useful methods that we can override: keyDown: and keyUp:. Here's the initial simplistic implementation of keyDown:
I removed the rotation code from the update: method and moved it to the keyDown: method... Now we have a spaceship that rotates.. Very slowly, because keyDown: is only called one time for about 5 updates:. We need to do something better. Somehow we need to move the rotation into the update: method.-(void) update:(NSTimeInterval)currentTime {}-(void)keyDown:(NSEvent *)theEvent {const CGFloat oneDegree = 2.0 * M_PI / 360.0;switch (theEvent.keyCode) {case 123:self.spaceship.zRotation += oneDegree;break;case 124:self.spaceship.zRotation -= oneDegree;break;default:break;}}
Let's make use of keyUp: method and a rotation property that we will add to our GameScene.
remember to add the keyPressed NSUInteger property int he GameScene interface block.-(void) update:(NSTimeInterval)currentTime {const CGFloat oneDegree = 2.0 * M_PI / 360.0;switch (self.keyPressed) {case 123:self.spaceship.zRotation += oneDegree;break;case 124:self.spaceship.zRotation -= oneDegree;break;default:break;}}-(void)keyDown:(NSEvent *)theEvent {self.keyPressed = theEvent.keyCode;}-(void)keyUp:(NSEvent *)theEvent {self.keyPressed = 0;}
It's better, the spaceship rotates a bit quicker, but pressing two keys at once will give us problems. For example, press and hold the left arrow key, our spaceship rotates counter-clockwise. Now, also press the right arrow key, our spaceship will rotate clockwise. Now release the right key, and the spaceship stops, even though the left key is still pressed...
What should happen? Let's write a specification:
- tapping a left, or right, arrow key must cause the spaceship to rotate and stop.
- tapping an holding the left arrow key must cause the spaceship to rotate counter-clockwise until the the left arrow key is released, at which point the rotation will stop.
- tapping an holding the right arrow key must cause the spaceship to rotate clockwise until the the left arrow key is released, at which point the rotation will stop.
- tapping and holding both arrow keys should not cause the spaceship to rotate as each rotation will cancel the other out.
- if the spaceship is being rotated by tapping and holding a left or right arrow key, tapping and holding the other key should cause the rotation of the spaceship to stop until one of the arrow keys is released.
We've got points 1, 2 and 3. Let's work on point 4.
Currently we keep track of only one key, let's fix that. Objective-C gives us many containers to choose from. We don't need to store multiple copies of our keypress, and we don't need them ordered. So NSSet seems to be the container to use, actually, since we want to add and remove keystrokes, NSMutableSet. Replace
with@property NSUInteger keyPressed;
and the keyDown: method becomes easy@property NSMutableSet* keysPressed;
the only wrinkle is that we need to remember that theEvent.keyCode is not an NSObject, but a POD unsigned short, so we need to wrap it in an NSNumber. I don't think I'd do this in production code. Using an NS containers may slow us down a bit, but for what we are doing here, this should be fine.-(void)keyDown:(NSEvent *)theEvent {[self.keysPressed addObject:[NSNumber numberWithUnsignedShort:theEvent.keyCode]];}
Let's look at the keyUp: method, it's a bit fancier..
aaaand there's one small thing I forgot.-(void)keyUp:(NSEvent *)theEvent {NSNumber* toRemove = [NSNumber numberWithUnsignedShort:theEvent.keyCode];for (NSNumber* n in self.keysPressed) {if ([toRemove isEqualToNumber:n]) {toRemove = n;break;}}[self.keysPressed removeObject:toRemove];}
Did you figure out what I missed? Yup, we need to instantiate _keysPressed. Add the line
in the didMoveToView: method, so that we have an object to add our keystrokes to._keysPressed = [NSMutableSet new];
Now specifications 4 and 5 are complete, without invalidating specifications 1, 2 and 3.
Our spaceship rotates.
This was a really long post, lets move the spaceship in the next post. But first, try doing it yourself. Here's what you need to know: the key code for the up arrow is 126. And to move the spaceship in the direction that the spaceship is point you'll need something like
we'll set that up next time. We'll also talk about making the motion a little bit more... I wanted to say realistic... but what I really mean is more like the game we're trying to recreate.CGPoint pt = _spaceship.position;pt.x += speed * cos(_spaceship.zRotation + c);pt.y += speed * sin(_spaceship.zRotation + c);_spaceship.position = pt;
Labels:
debugger,
keyDown,
keyUp,
lldb,
NSBezierPath,
NSEvent,
NSImage,
NSMutableSet,
OS X,
SKSpriteNode,
SpriteKit
Friday, November 6, 2015
My Experiments with SpriteKit: Part Three - Textures!
Let's create an interesting sprite from the ground up. We'll do everything!
Start with the code we've written already. Remove the sprite code that we've written, even remove the image from the Assets.xcassets folder. Your GameScene should look like this:
What does our scene need? It needs a border...
Let's make a nice gold (RED=255, GREEN=215, BLUE=0) border. It should hug the scene frame, have rounded inside corners and be 15 points thick.
SKSpriteKit has a couple of methods we can use to create our sprite: spriteNodeWithImageNamed: and spriteNodeWithTexture:. The first one is great if we already have a named image, for example we may already have an image that we have placed in our Assets.xcassets folder. And the second one is great if we have a Texture of type SKTexture.
Texture objects manage graphics resources that can be applied to our sprites when they are being rendered. Let's use spriteNodeWithTexture: method to create a sprite that we can use as a border to our scene. Sometimes it's necessary to work backwards towards our goal, let's try that.
In order to draw in the image we need to obtain a graphics context for the image, this turns out to be pretty easy with the NSImage method lockFocus. When we're done drawing our image, we'll call unlockFocus.
Finally, call the fill method on our path. Here's the application:
A very nice border. Let's add one more sprite to set us up for the next blog post in this series. But first, a bit of refactoring. The didMoveToView: method is getting a bit ungainly:
On to the new sprite! I want a retro game style spaceship... Here's the drawing code:
In part four we'll let the user rotate the space ship and move it around.
Start with the code we've written already. Remove the sprite code that we've written, even remove the image from the Assets.xcassets folder. Your GameScene should look like this:
I've also set the background colour of the scene to black. Build and run this and you'll get an application with a black window.#import "GameScene.h"@interface GameScene ()@property CGPoint center;@property CGFloat height;@end@implementation GameScene-(void)didMoveToView:(SKView *)view {self.backgroundColor = [NSColor blackColor];_height = CGRectGetHeight(self.frame);_center = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));}-(void)update:(CFTimeInterval)currentTime {}@end
What does our scene need? It needs a border...
Let's make a nice gold (RED=255, GREEN=215, BLUE=0) border. It should hug the scene frame, have rounded inside corners and be 15 points thick.
SKSpriteKit has a couple of methods we can use to create our sprite: spriteNodeWithImageNamed: and spriteNodeWithTexture:. The first one is great if we already have a named image, for example we may already have an image that we have placed in our Assets.xcassets folder. And the second one is great if we have a Texture of type SKTexture.
Texture objects manage graphics resources that can be applied to our sprites when they are being rendered. Let's use spriteNodeWithTexture: method to create a sprite that we can use as a border to our scene. Sometimes it's necessary to work backwards towards our goal, let's try that.
So we need a SKTexture. Let's add one of those:-(void)didMoveToView:(SKView *)view {self.backgroundColor = [NSColor blackColor];_height = CGRectGetHeight(self.frame);_center = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));SKSpriteNode* border;border = [SKSpriteNode spriteNodeWithTexture:<#(nullable SKTexture *)#>];border.position = self.center;[self addChild:border];}
Right, now we need an NSImage:SKSpriteNode* border;SKTexture* texture = [SKTexture textureWithImage:<#(nonnull NSImage *)#>];border = [SKSpriteNode spriteNodeWithTexture:texture];border.position = self.center;[self addChild:border];
OK, we're back to a build-able and run-able project. But it's still a black window. We have a sprite, having a texture that contains a image that has the same size as the window. Maybe... Let's find out by drawing the border.SKSpriteNode* border;NSImage* image;image = [[NSImage alloc] initWithSize:self.frame.size];// ???SKTexture* texture = [SKTexture textureWithImage:image];border = [SKSpriteNode spriteNodeWithTexture:texture];border.position = self.center;[self addChild:border];
In order to draw in the image we need to obtain a graphics context for the image, this turns out to be pretty easy with the NSImage method lockFocus. When we're done drawing our image, we'll call unlockFocus.
We just need to figure out how to draw the border. It's easy: use NSBezierPath to describe the outline of our border and then fill it in.NSImage* image;image = [[NSImage alloc] initWithSize:self.frame.size];[image lockFocus];// ???[image unlockFocus];
That defines the path we want to fill: a large rect that is the size of view, and a smaller rounded rect with the same centre 15 points inside of the large rect. Now, we need to apply the path, with the gold colour to the image.[image lockFocus];NSBezierPath* path = [NSBezierPath bezierPathWithRect:self.frame];[path appendBezierPathWithRoundedRect:CGRectInset(self.frame, 15, 15)xRadius:25yRadius:25];// ???[image unlockFocus];
the set method, called on an NSColor, sets the drawing fill and stroke colour to that of the receiver. The windingRule applies to the fill method that we use to actually draw the path and fill it with the gold colour. I chose the NSEvenOddWindingRule simply because it caused the fill to work correctly, inside the two rectangles as opposed to filling both rectangles (actually, there's a good reason to use NSEvenOddWindingRule, I explain why in another post).[image lockFocus];NSBezierPath* path = [NSBezierPath bezierPathWithRect:self.frame];[path appendBezierPathWithRoundedRect:CGRectInset(self.frame, 15, 15)xRadius:25yRadius:25];[[NSColor colorWithRed:(255.0/255.0)green:(215.0/255.0)blue:(0.0/255.0)alpha:1.0] set];path.windingRule = NSEvenOddWindingRule;[path fill];[image unlockFocus];
Finally, call the fill method on our path. Here's the application:
A very nice border. Let's add one more sprite to set us up for the next blog post in this series. But first, a bit of refactoring. The didMoveToView: method is getting a bit ungainly:
So we should pull the border creation code out into it's own method.-(void)didMoveToView:(SKView *)view {self.backgroundColor = [NSColor blackColor];_height = CGRectGetHeight(self.frame);_center = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));SKSpriteNode* border;NSImage* image;image = [[NSImage alloc] initWithSize:self.frame.size];[image lockFocus];NSBezierPath* path = [NSBezierPath bezierPathWithRect:self.frame];[path appendBezierPathWithRoundedRect:CGRectInset(self.frame, 15, 15)xRadius:25yRadius:25];[[NSColor colorWithRed:(255.0/255.0)green:(215.0/255.0)blue:(0.0/255.0)alpha:1.0] set];path.windingRule = NSEvenOddWindingRule;[path fill];[image unlockFocus];SKTexture* texture = [SKTexture textureWithImage:image];border = [SKSpriteNode spriteNodeWithTexture:texture];border.position = self.center;[self addChild:border];}
Now we can create the border in didMoveToView with just one line.-(void)addBorderofThickness:(CGFloat)t andCornerRadius:(CGFloat)cr {SKSpriteNode* border;NSImage* image;image = [[NSImage alloc] initWithSize:self.frame.size];[image lockFocus];NSBezierPath* path = [NSBezierPath bezierPathWithRect:self.frame];[path appendBezierPathWithRoundedRect:CGRectInset(self.frame, t, t)xRadius:cryRadius:cr];[[NSColor colorWithRed:(255.0/255.0)green:(215.0/255.0)blue:(0.0/255.0)alpha:1.0] set];path.windingRule = NSEvenOddWindingRule;[path fill];[image unlockFocus];SKTexture* texture = [SKTexture textureWithImage:image];border = [SKSpriteNode spriteNodeWithTexture:texture];border.position = self.center;[self addChild:border];}
On to the new sprite! I want a retro game style spaceship... Here's the drawing code:
Go ahead and make the sprite, you'll get something like:NSImage* image;image = [[NSImage alloc] initWithSize:CGSizeMake(30, 30)];[image lockFocus];NSBezierPath* path = [NSBezierPath bezierPath];path.lineWidth = 2.5;[path moveToPoint:CGPointMake(0, 0)];[path lineToPoint:CGPointMake(15, 30)];[path lineToPoint:CGPointMake(30, 0)];[path lineToPoint:CGPointMake(15, 10)];[path closePath];[[NSColor colorWithRed:(255.0/255.0)green:(215.0/255.0)blue:(0.0/255.0)alpha:1.0] set];[path stroke];[image unlockFocus];
In part four we'll let the user rotate the space ship and move it around.
Labels:
NSBezierPath,
NSImage,
OS X,
SKSpriteNode,
SpriteKit
Subscribe to:
Posts (Atom)