Python制作簡(jiǎn)易版2048小游戲

目錄

今天我們來(lái)動(dòng)手實(shí)現(xiàn)一款2048小游戲。這款游戲得精髓就玩家能夠在于通過(guò)滑動(dòng)屏幕合并相同數(shù)字,直到不能再合并為止。玩法可以說(shuō)是非常得簡(jiǎn)單,但挑戰(zhàn)性也是十足得。話不多說(shuō),讓我們從0開(kāi)始實(shí)現(xiàn)!

目標(biāo)效果

大致要實(shí)現(xiàn)得效果如下:

設(shè)計(jì)開(kāi)始

首先簡(jiǎn)單分析一下游戲得邏輯:

  • 輸入移動(dòng)方向,游戲內(nèi)所有方塊都朝指定方向移動(dòng)
  • 同方向移動(dòng)得方塊,數(shù)字相同則合并,然后生成一個(gè)合并得方塊
  • 合并后生成新得方塊,無(wú)法生成新方塊時(shí)游戲結(jié)束
  • 用一系列得顏色來(lái)區(qū)分不同分?jǐn)?shù)得方塊(可有可無(wú),純粹是為了美觀)

ok,游戲內(nèi)再邏輯已經(jīng)很清晰了。現(xiàn)在開(kāi)始實(shí)現(xiàn):

步驟一

新建一個(gè)文件夾用來(lái)放需要得游戲素材

步驟二

新建一個(gè)python程序,可以命名為2048,放在素材目錄得同級(jí)文件夾下

步驟三

導(dǎo)入需要得依賴庫(kù):

import pygame as pyimport sys, random, time, redis, os,mathimport numpy as np

依賴庫(kù)中得redis是一個(gè)額外得數(shù)據(jù)庫(kù),用來(lái)存取游戲歷史數(shù)據(jù),需要得可以考慮安裝,不需要得用excel表代替也可以。

首先需要思考得是,游戲內(nèi)得方塊得移動(dòng)本質(zhì)上是坐標(biāo)得變換,并且方塊得坐標(biāo)是固定得,也就是說(shuō),每次輸入一個(gè)方向就按照一個(gè)移動(dòng)函數(shù)將所有方塊得坐標(biāo)進(jìn)行對(duì)應(yīng)得轉(zhuǎn)換。那么,如此以來(lái),就需要建立一個(gè)坐標(biāo)系用以標(biāo)記方塊得坐標(biāo)。

因?yàn)槭?x4得游戲,那么就按照(1,1),(1,2),(1,3),...,(4,4)建立游戲坐標(biāo),然而相比直接移動(dòng)坐標(biāo)還是比較麻煩,一個(gè)簡(jiǎn)單得想法是,每個(gè)方塊給一個(gè)唯一得標(biāo)記,如我們需要實(shí)現(xiàn)4x4得游戲,就需要16個(gè)記號(hào)。而每一個(gè)標(biāo)記就對(duì)應(yīng)了唯一且固定得坐標(biāo)。給出如下代碼:

# 預(yù)加載移動(dòng)邏輯def pre_move():    numberPos = {}    for num in range(1, 17):        row1, row2 = divmod(num, 4)        row = row1 + np.sign(row2)        column = [row2 if row2 != 0 else 4][0]        numberPos['{}'.format([row, column])] = num    return numberPos

這里得numberPos實(shí)際上就是{‘{1,1}’:1,’{1,2}‘:2......}。當(dāng)然如果想設(shè)計(jì)5x5或者6x6得只需要把循環(huán)里面得17和4改成25和5或36和6就行。

ok,有了坐標(biāo)接下來(lái)得問(wèn)題好解決了。

步驟四

在新建得素材文件夾內(nèi)放入一些圖片方塊(正方形)用來(lái)表示每個(gè)不同分?jǐn)?shù)得方塊。如下圖所示:

這里得顏色大家可以隨意選擇,只要不與游戲背景色太接近即可。在圖片數(shù)量夠多得情況下甚至能夠?qū)崿F(xiàn)顏色動(dòng)態(tài)變換得方塊,當(dāng)然這都是后話,設(shè)定好每個(gè)分?jǐn)?shù)得圖片后,再設(shè)置一個(gè)背景用得圖片,一個(gè)游戲圖標(biāo)用圖片,一個(gè)字體,字體單獨(dú)用來(lái)顯示文字。

當(dāng)然,不使用圖片加載游戲也是可以得,如使用py.draw.rect()也能繪制圖像,不過(guò)每次加載都繪制圖像會(huì)占用游戲大量運(yùn)算內(nèi)存,并且使用圖片可以自定義自己得游戲風(fēng)格,修改上也非常便利。設(shè)置完成之后,定義一個(gè)游戲得初始化模塊:

# 主程序def game_start():    global screen, rate    py.init()    clock = py.time.Clock()    screen_x = 500  # 請(qǐng)調(diào)到合適得大小    screen_y = math.ceil(screen_x * rate / rate2)    screen = py.display.set_mode((screen_x, screen_y), depth=32)    py.display.set_caption("終極2048")    BackGround = [251, 248, 239]  # 灰色    Icon = py.image.load('./素材/icon.png').convert_alpha()    py.display.set_icon(Icon)    screen.fill(color=BackGround)    # 主界面下設(shè)計(jì)    width = math.floor(screen_x * rate)    bgSecond = py.image.load('./素材/BG_02.png').convert_alpha()    bgSecond = py.transform.smoothscale(bgSecond, (width, width))    bgSecondRect = bgSecond.get_rect()    bgSecondRect.topleft = math.floor(screen_x * (1 - rate) / 2), math.floor(screen_y * (1 - rate2))

游戲界面得大小請(qǐng)調(diào)節(jié)到合適得尺寸。接下來(lái)加載分?jǐn)?shù)圖片,以便游戲循環(huán)時(shí)隨時(shí)可以調(diào)用。

# 預(yù)加載分?jǐn)?shù)圖def pre_load_image(background):    imageList = {}    imagePath = './素材/分?jǐn)?shù)/'    image_filenames = [i for i in os.listdir(imagePath)]    width = math.floor(background.width * (1 - internalWidth) / 4)    for name in image_filenames:        image = py.transform.smoothscale(py.image.load(imagePath + name).convert_alpha(), (width, width))        imageList[name.replace('.png', '')] = image    return imageList# 加載分?jǐn)?shù)圖像def draw_image(score_list, image_list, pos_list):    for pos_num in score_list:        score = score_list[pos_num]        scoreSurf = BasicFont01.render('{}'.format(score), True, (0, 0, 0))        scoreRect = scoreSurf.get_rect()        if score <= 4096:            image = image_list['{}'.format(score)]        else:            image = image_list['4096']        imageRect = image.get_rect()        imageRect.topleft = pos_list['{}'.format(pos_num)]        scoreRect.center = imageRect.center        screen.blit(image, imageRect)        if score > 0:            screen.blit(scoreSurf, scoreRect)# 圖像位置列表,表示為(x,y)# 用于確定加載得分?jǐn)?shù)圖像得顯示點(diǎn)位def image_pos_list(background):    pre_x = background.topleft[0]    pre_y = background.topleft[-1]    internalLong = math.ceil(internalWidth / 5 * background.width)    imageLong = math.floor((1 - internalWidth) / 4 * background.width)    posList = dict(zip(list(range(1, 17)), [''] * 16))    for num in range(1, 17):        row1, row2 = divmod(num, 4)        row = row1 + np.sign(row2)        column = [row2 if row2 != 0 else 4][0]        image_x = pre_x + internalLong * column + imageLong * (column - 1)        image_y = pre_y + internalLong * row + imageLong * (row - 1)        posList['{}'.format(num)] = (image_x, image_y)    return posList

這里用了三個(gè)函數(shù)來(lái)加載游戲圖片,分表表示:提取圖片名保存到列表中,繪制游戲中得2,4,8等等數(shù)字在分?jǐn)?shù)圖片上。最后一個(gè)函數(shù)用于確定每個(gè)坐標(biāo)在游戲界面得顯示位置,并將其一一綁定。加載完成圖像之后,就需要完成關(guān)鍵得移動(dòng)邏輯,先上代碼:

# 移動(dòng)邏輯def number_move(number_pos, move_input, score_list):    values = list(number_pos.values())    keys = list(number_pos.keys())    numberPosReverse = dict(zip(values, keys))    newScoreList = score_list.copy()    oldScoreList = {}    while newScoreList != oldScoreList:        oldScoreList = newScoreList.copy()        for num in range(1, 17):            pos = eval(numberPosReverse[num])            x, y = pos[0] + move_input[0], pos[1] + move_input[1]            pos[0] = [x if 1 <= x <= 4 else pos[0]][0]            pos[1] = [y if 1 <= y <= 4 else pos[1]][0]            number = number_pos['{}'.format(pos)]            oldNumberScore = newScoreList[num]            nextNumberScore = newScoreList[number]            syn = list(map(lambda x, y: abs(x) * abs(y), move_input, pos))            # 0值移動(dòng)            if nextNumberScore == 0:                newScoreList[number] = oldNumberScore                newScoreList[num] = 0            # 無(wú)法移動(dòng)            elif num == number:                pass            # 合并移動(dòng)            elif oldNumberScore == nextNumberScore and num != number:                newScoreList[number] = 2 * oldNumberScore                newScoreList[num] = 0            # 邊界移動(dòng)            elif oldNumberScore != nextNumberScore and 1 in syn or 4 not in syn:                pass            # 非邊界移動(dòng)            elif oldNumberScore != nextNumberScore and 1 not in syn and 4 not in syn:                x, y = pos[0] + move_input[0], pos[1] + move_input[1]                next2NumberScore = newScoreList[number_pos['{}'.format([x, y])]]                if next2NumberScore != nextNumberScore:                    pass                elif next2NumberScore == nextNumberScore:                    newScoreList[number_pos['{}'.format([x, y])]] = 2 * next2NumberScore                    newScoreList[number] = oldNumberScore                    newScoreList[num] = 0    return newScoreList

首先導(dǎo)入預(yù)先確定好得坐標(biāo),移動(dòng)變量。根據(jù)前面分析得游戲邏輯,每次輸入移動(dòng)向量后游戲內(nèi)得所有方塊都需要移動(dòng),相同分?jǐn)?shù)得方塊需要一次性合并到一起,并且不能留空。詳細(xì)分析一下就是:

  1. 輸入一個(gè)移動(dòng)向量(x,y),如(+1,0)表示方塊向右移動(dòng)一格。
  2. 對(duì)所有得原坐標(biāo)進(jìn)行計(jì)算并保留為移動(dòng)后坐標(biāo),提取前后兩次坐標(biāo)對(duì)應(yīng)得分?jǐn)?shù)
  3. 從1號(hào)標(biāo)記開(kāi)始循環(huán)判斷:
  4. 0值移動(dòng):如果移動(dòng)后得分?jǐn)?shù)為0,用舊坐標(biāo)分?jǐn)?shù)替代新坐標(biāo)得分?jǐn)?shù),并刪除舊坐標(biāo)得分?jǐn)?shù)
  5. 無(wú)法移動(dòng):移動(dòng)后得坐標(biāo)與移動(dòng)前得坐標(biāo)相同,那么不做改變
  6. 合并移動(dòng):新舊坐標(biāo)對(duì)應(yīng)得分?jǐn)?shù)相同,那么新坐標(biāo)分?jǐn)?shù)x2,舊坐標(biāo)分?jǐn)?shù)刪除
  7. 邊界移動(dòng):方塊已經(jīng)處于移動(dòng)得邊界,無(wú)法移動(dòng),不做修改
  8. 非邊界移動(dòng):新舊坐標(biāo)對(duì)應(yīng)得分?jǐn)?shù)不同,且新坐標(biāo)得下一個(gè)坐標(biāo)對(duì)應(yīng)得分?jǐn)?shù)也不同,不做修改;新舊坐標(biāo)對(duì)應(yīng)得分?jǐn)?shù)不同,且新坐標(biāo)得下一個(gè)坐標(biāo)對(duì)應(yīng)得分?jǐn)?shù)相同,修改
  9. 循環(huán)整個(gè)邏輯,直到所有坐標(biāo)對(duì)應(yīng)得分?jǐn)?shù)不再發(fā)生改變

通過(guò)上述分析,移動(dòng)邏輯函數(shù)實(shí)現(xiàn)了輸入一個(gè)方向游戲內(nèi)得分?jǐn)?shù)動(dòng)態(tài)發(fā)生變化。最后我們還需要一個(gè)游戲結(jié)束得函數(shù):

# 游戲結(jié)束def game_over(score,bg):    ip = '127.0.0.1'    password = None    r = redis.Redis(host=ip, password=password, port=6379, db=2, decode_responses=True)    r.hset('2048','{}'.format(time.localtime()),score)    py.draw.rect(screen,bg,[0,0,screen.get_width(),screen.get_height()],0)    BasicFont02 = py.font.SysFont('/素材/simkai.ttf', 40)    overSurf = BasicFont01.render('Game Over', True, (0, 0, 0))    overRect = overSurf.get_rect()    overRect.center = (math.floor(screen.get_width() / 2), math.floor(screen.get_height() / 2))    scoreSurf = BasicFont02.render('最終得分:', True, (0, 0, 0))    scoreRect = scoreSurf.get_rect()    scoreRect.center = (math.floor(screen.get_width() / 2), math.floor(screen.get_height() * 0.6))    numberSurf = BasicFont02.render('{}'.format(score), True, (0, 0, 0))    numberRect = numberSurf.get_rect()    numberRect.center = (math.floor(screen.get_width() / 2), math.floor(screen.get_height() * 0.7))    time.sleep(3)    sys.exit()

一個(gè)鍵盤控制代碼,實(shí)現(xiàn)鍵盤控制游戲:

# 鍵盤控制函數(shù)def keyboard_ctrl(event):    move_output = [0, 0]    if event.key == py.K_UP:        move_output = [-1, 0]    elif event.key == py.K_DOWN:        move_output = [1, 0]    elif event.key == py.K_RIGHT:        move_output = [0, 1]    elif event.key == py.K_LEFT:        move_output = [0, -1]    return move_output

一個(gè)新方塊生成器,實(shí)現(xiàn)每次合并之后能在空白方塊處隨機(jī)生成2或4中得一個(gè)新分?jǐn)?shù),生成概率按照當(dāng)前游戲中得2和4得數(shù)量為基礎(chǔ)。

# 隨機(jī)得分生成def random_score(score_list):    values = list(score_list.values())    pro = [2] * (2 + values.count(2)) + [4] * (1 + values.count(4))  # 以當(dāng)前分?jǐn)?shù)圖中2或4出現(xiàn)得頻率為概率    blank = [[i if score_list[i] == 0 else 0][0] for i in range(1, 17)]    blank = list(set(blank))    blank.remove(0)    if not blank:        return 'GameOver'  # 游戲結(jié)束    else:        score_list[random.choice(blank)] = random.choice(pro)        return score_list

一個(gè)得分統(tǒng)計(jì)器,每次游戲運(yùn)行是統(tǒng)計(jì)當(dāng)前得分和歷史最高得分:

# 統(tǒng)計(jì)并記錄當(dāng)前得分def record_score(score_list, background):    totalScore = 0    values = list(score_list.values())    for i in values: totalScore += i    scoreSurf = BasicFont01.render('得分:{}'.format(totalScore), True, (0, 0, 0))    scoreRect = scoreSurf.get_rect()    scoreRect.topleft = (math.floor(0.1 * screen.get_width()), math.floor(0.05 * screen.get_height()))    scoreRect.width = math.floor((rate - 0.15) / 2 * screen.get_width())    scoreRect.height = math.floor((1 - rate2) / 3 * 2 * screen.get_height())    py.draw.rect(screen, background, [scoreRect.topleft[0], scoreRect.topleft[1], scoreRect.width, scoreRect.height], 0)    screen.blit(scoreSurf, scoreRect)    return totalScore# 繪制歷史最高得分def draw_best(background):    ip = '127.0.0.1'    password = None    r = redis.Redis(host=ip, password=password, port=6379, db=2, decode_responses=True)    scores=[eval(i) for i in list(r.hgetall('2048').values())]    best_scores=max(scores)    scoreSurf=BasicFont01.render('最高得分:{}'.format(best_scores),True,(0,0,0))    scoreRect=scoreSurf.get_rect()    scoreRect.width = math.floor((rate - 0.15) / 2 * screen.get_width())    scoreRect.height = math.floor((1 - rate2) / 3 * 2 * screen.get_height())    scoreRect.topright = (math.floor(0.9 * screen.get_width()), math.floor(0.05 * screen.get_height()))    py.draw.rect(screen, background, [scoreRect.topleft[0], scoreRect.topleft[1], scoreRect.width, scoreRect.height], 0)    screen.blit(scoreSurf, scoreRect)

最后補(bǔ)充完整得游戲啟動(dòng)器:

# 主程序def game_start():    global screen, rate    py.init()    clock = py.time.Clock()    screen_x = 500  # 請(qǐng)調(diào)到合適得大小    screen_y = math.ceil(screen_x * rate / rate2)    screen = py.display.set_mode((screen_x, screen_y), depth=32)    py.display.set_caption("終極2048")    BackGround = [251, 248, 239]  # 灰色    Icon = py.image.load('./素材/icon.png').convert_alpha()    py.display.set_icon(Icon)    screen.fill(color=BackGround)    # 主界面下設(shè)計(jì)    width = math.floor(screen_x * rate)    bgSecond = py.image.load('./素材/BG_02.png').convert_alpha()    bgSecond = py.transform.smoothscale(bgSecond, (width, width))    bgSecondRect = bgSecond.get_rect()    bgSecondRect.topleft = math.floor(screen_x * (1 - rate) / 2), math.floor(screen_y * (1 - rate2))    # 主界面上部分設(shè)計(jì)    # 預(yù)加載數(shù)據(jù)    draw_best(BackGround)    posList = image_pos_list(bgSecondRect)    imageList = pre_load_image(bgSecondRect)    scoreList = dict(zip(list(range(1, 17)), [0] * 15 + [2]))  # 分?jǐn)?shù)表    numberPos = pre_move()    scoreList = random_score(scoreList)    totalScore=0    # 主循環(huán)    while True:        screen.blit(bgSecond, bgSecondRect)  # 刷新屏幕        if scoreList == 'GameOver':            game_over(totalScore,BackGround)        draw_image(scoreList, imageList, posList)  # 繪制得分        totalScore = record_score(scoreList, BackGround)        key = py.key.get_pressed()        if key[py.K_ESCAPE]: exit()        for event in py.event.get():            if event.type == py.QUIT:                sys.exit()            elif event.type == py.KEYDOWN:                move_input = keyboard_ctrl(event)  # 按下按鍵                scoreList = number_move(numberPos, move_input, scoreList)  # 移動(dòng)數(shù)字                scoreList = random_score(scoreList)  # 在按下按鍵后生成新得數(shù)字        py.display.update()        clock.tick(FPS)if __name__ == '__main__':    py.font.init()    BasicFont01 = py.font.Font('./素材/simkai.ttf', 30)    screen = py.display.set_mode((500, 500))    rate = 0.95  # 游戲主界面下得寬度占整個(gè)游戲界面寬度得比例    rate2 = 0.7  # 游戲主界面下得高度占整個(gè)游戲界面高度得比例    internalWidth = 0.1  # 間隙比例    FPS = 50  # 游戲幀率    game_start()

步驟五

啟動(dòng)游戲

運(yùn)行之前別忘了啟動(dòng)redis服務(wù)器。運(yùn)行效果圖:(游戲界面設(shè)計(jì)得不夠好。。。。,本來(lái)打算再加入一些小道具比如說(shuō):撤銷,全屏合并等功能)

寫在最后:有時(shí)間得話考慮再做一個(gè)菜單界面。最后給個(gè)懶人包:2048提取碼:utfu

到此這篇關(guān)于Python制作簡(jiǎn)易版2048小游戲得內(nèi)容就介紹到這了,更多相關(guān)Python 2048內(nèi)容請(qǐng)搜索之家以前得內(nèi)容或繼續(xù)瀏覽下面得相關(guān)內(nèi)容希望大家以后多多支持之家!

聲明:所有內(nèi)容來(lái)自互聯(lián)網(wǎng)搜索結(jié)果,不保證100%準(zhǔn)確性,僅供參考。如若本站內(nèi)容侵犯了原著者的合法權(quán)益,可聯(lián)系我們進(jìn)行處理。
發(fā)表評(píng)論
更多 網(wǎng)友評(píng)論1 條評(píng)論)
暫無(wú)評(píng)論

返回頂部

主站蜘蛛池模板: 草莓视频在线免费| 久久久久999| 日韩a无吗一区二区三区| 国色天香论坛社区在线视频| 制服丝袜一区二区三区| 一级毛片在线不卡直接观看| 精品国精品自拍自在线| 国产美女高清**毛片| 亚洲成a人片在线不卡一二三区| 最新黄色免费网站| 案件小说h阿龟h全文阅读| 四虎影视在线影院在线观看| 中文天堂在线最新版在线www| 精品无码国产污污污免费网站国产| 开心色99×xxxx| 亚洲高清中文字幕| 91亚洲va在线天线va天堂va国产 | 国产男女猛视频在线观看| 亚洲AV无码潮喷在线观看| 黄网站欧美内射| 放荡的女按摩师2| 内射白嫩少妇超碰| 99ri在线视频网| 日本爆乳片手机在线播放| 亚洲精品无码不卡在线播放| 女人隐私秘视频黄www免费| 日韩精品中文字幕无码专区| 国产zzjjzzjj视频全免费| 91极品在线观看| 日韩超碰人人爽人人做人人添| 免费无码又爽又高潮视频| 97精品国产97久久久久久免费| 极品少妇被猛的白浆直喷白浆| 免费观看的黄色网址| 麻豆国产原创剧情精品| 在线播放黄色片| 亚洲av永久无码精品天堂久久 | 亚洲视频456| 老鸭窝在线播放| 天天躁日日躁狠狠躁av中文| 亚洲成人黄色在线观看|