Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Cloud Jump Game
-
Maybe @Sebastian can help
-
@ccc: Not perfect, but I think you ...
import Image, ImageDraw, random, scene def generate_shapes(num_circles): shapes = [] for i in xrange(num_circles): x = i * 20 - ((num_circles/2)*30) #range: -74 .. +40 y = (random.random()-0.5) * 30 #range: -15 .. +15 x += 75 # this is a hack! y += 20 # this is a hack! rad = random.randint(50, 100) #range: +50 .. +100 shapes.append([x, y, rad]) return shapes def cloud_maker(): num_circles = 5 #range: 4..5 => I prefer 5 :) #image_size = ((num_circles + 1) * 30, 60) image_size = (214, 140) theImage = Image.new('RGBA', image_size) #, 'pink') draw = ImageDraw.Draw(theImage) circles = generate_shapes(num_circles) for i in circles: bbox = (i[1] - 5, i[0], i[2], i[2]) # x/y swap draw.ellipse(bbox, fill='rgb(90%,90%,90%)') for i in circles: bbox = (i[1] + 5, i[0], i[2], i[2]) # x/y swap draw.ellipse(bbox, fill='white') del draw return theImage class Cloud(scene.Layer): def __init__(self, parent = None): cloud_image = cloud_maker() super(self.__class__, self).__init__(scene.Rect(*cloud_image.getbbox())) if parent: parent.add_layer(self) self.image = scene.load_pil_image(cloud_image) class MyScene(scene.Scene): def __init__(self): scene.run(self) def setup(self): self.cloud = Cloud(self) self.cloud.frame.x = self.bounds.w * 0.8 # x/y swap self.cloud.frame.y = 0 def draw(self): scene.background(0.40, 0.80, 1.00) self.root_layer.update(self.dt) self.root_layer.draw() self.cloud.frame.y -= 1 # x/y swap if not self.bounds.intersects(self.cloud.frame): del self.cloud # whack the old cloud self.setup() # and create a new one MyScene()
-
@brumm. Thanks for this but the PIL-based clouds are still not as beautiful as the ones in the game...
Three key attributes of the game clouds are missing:
- The long axis is generally the y axis instead if the x axis (this makes clouds more inviting to land on)
- The grey (silver!) lining is on the underside of the clouds (this gives them shading, depth, and believability)
- They are not as big and beautiful and believable as the original clouds.
My hacks at this problem have been fruitless.
-
I think I'm getting there! The ImageDraw.ImageDraw.ellipse function doesn't use a width and height value in its bounding box, but rather x and y values. So maybe something like this?
import Image, ImageDraw, random, scene def generate_shapes(num_circles): shapes = [] for i in xrange(num_circles): x = (i * 20 - ((num_circles/2)*30))+90 y = ((random.random()-0.5) * 30)+15 rad = random.randint(50, 100) shapes.append([x, y, rad]) return shapes def cloud_maker(): num_circles = random.randint(5, 6) image_size = (220, 140) theImage = Image.new('RGBA', image_size) draw = ImageDraw.Draw(theImage) circles = generate_shapes(num_circles) for i in circles: r = i[2] bbox = (i[0], 40-i[1], i[0]+r, 40-i[1]+r) draw.ellipse(bbox, fill='rgb(90%,90%,90%)') for i in circles: r = i[2] bbox = (i[0], 40-i[1]-10, i[0]+r, 40-i[1]+r-10) draw.ellipse(bbox, fill='white') del draw return theImage class Cloud(scene.Layer): def __init__(self, parent = None): cloud_image = cloud_maker() super(self.__class__, self).__init__(scene.Rect(*cloud_image.getbbox())) if parent: parent.add_layer(self) self.image = scene.load_pil_image(cloud_image) class MyScene(scene.Scene): def setup(self): self.cloud = Cloud(self) self.cloud.frame.x = self.bounds.w * 0.5 self.cloud.frame.y = self.bounds.h*0.8 def draw(self): scene.background(0.40, 0.80, 1.00) self.root_layer.update(self.dt) self.root_layer.draw() scene.rect(*self.cloud.frame) def touch_began(self, touch): self.root_layer.remove_layer(self.cloud) self.cloud = Cloud(self) self.cloud.frame.x = self.bounds.w * 0.5 self.cloud.frame.y = self.bounds.h*0.8 scene.run(MyScene())
-
PIL coordinate system is also flipped in y, so you need to use image height minus y.
Scene allowed you to draw outside of the frame, PIL does not. So you need to add 60/15 respectively so the cords are Zero based. Image size should be 200 wide (5th cloud starts at 80, and is 100 wide), and 140 tall (0 to 30 random height, plus 10 for shadow offset).
Then top would beh-i[1]
, bottom would beh-i[1]-i[2]
, and you would subtract 10 for both to shift gray cloud down. -
@JonB Thanks! I edited my post above. It seems to work ok now ;)
-
You guys are awesome. Thanks much.
-
OK guys... One issue remains :-(
The image frame is larger than the cloud itself. See: https://github.com/tjferry14/Cloud-Jump-2/issues/37
-
I found a solution that may work. @ccc What do you think?
import Image, ImageDraw, random, scene import numpy as np class Cloud(scene.Layer): def __init__(self, parent = None): cloud_image = self.create_image() super(self.__class__, self).__init__(scene.Rect(*cloud_image.getbbox())) if parent: parent.add_layer(self) self.image = scene.load_pil_image(cloud_image) def generate_shapes(self, num_circles): shapes = [] for i in xrange(num_circles): x = (i * 20 - ((num_circles/2)*30))+90 y = ((random.random()-0.5) * 30)+15 rad = random.randint(50, 100) shapes.append([x, y, rad]) return shapes # found on 'http://stackoverflow.com/questions/14211340/automatically-cropping-an-image-with-python-pil' def crop_image(self, img): image_data = np.asarray(img) image_data_bw = image_data.max(axis=2) non_empty_columns = np.where(image_data_bw.max(axis=0)>0)[0] non_empty_rows = np.where(image_data_bw.max(axis=1)>0)[0] cropBox = (min(non_empty_rows), max(non_empty_rows), min(non_empty_columns), max(non_empty_columns)) image_data_new = image_data[cropBox[0]:cropBox[1]+1, cropBox[2]:cropBox[3]+1, :] img = Image.fromarray(image_data_new) return img def create_image(self): num_circles = random.randint(5, 6) image_size = (220, 140) theImage = Image.new('RGBA', image_size) draw = ImageDraw.Draw(theImage) circles = self.generate_shapes(num_circles) for i in circles: r = i[2] bbox = (i[0], 40-i[1], i[0]+r, 40-i[1]+r) draw.ellipse(bbox, fill='rgb(90%,90%,90%)') for i in circles: r = i[2] bbox = (i[0], 40-i[1]-10, i[0]+r, 40-i[1]+r-10) draw.ellipse(bbox, fill='white') del draw return self.crop_image(theImage) class MyScene(scene.Scene): def setup(self): self.cloud = Cloud(self) self.cloud.frame.x = self.bounds.w * 0.5 self.cloud.frame.y = self.bounds.h*0.8 def draw(self): scene.background(0.40, 0.80, 1.00) scene.fill(0,0,0) scene.rect(*self.cloud.frame) self.root_layer.update(self.dt) self.root_layer.draw() def touch_began(self, touch): self.root_layer.remove_layer(self.cloud) self.cloud = Cloud(self) self.cloud.frame.x = self.bounds.w * 0.5 self.cloud.frame.y = self.bounds.h*0.8 scene.run(MyScene())
-
@Sebastian thank you so much for your help with this project. Will add you to the contributors list.
-
@techteej No worries! That's what I love about this forum; you help out if you can, and in return people will help you when you need it :)
-
A study on player death
was created for those interested to helps us give the game an arcade-like feel. A great solution would close out issue #9. https://github.com/tjferry14/Cloud-Jump-2/blob/master/etude_on_player_death.py -
Link updated from ccc's post: https://github.com/tjferry14/Cloud-Jump-2
-
Any animation experts out there with scene that can help us out?
-
Can hack or not?
After a period of learning I was able to hack this game with Lucky Patcher application. Please use Lucky Patcher Playstation with many features from this address: https://luckypatcherofficial.com/download/lucky-patcher-on-playstation/ -
I just made a
from six import StringIO
change to the code at https://github.com/tjferry14/Cloud-Jump-2 to get the Travis CI tests to pass. I have not run this in a long time so if you find that other changes are needed, please open a pull request.