)
前言首先我們需要明確對(duì)于pygame框架是由SDL寫(xiě)出來(lái)的而SDL是OpenGL一步步寫(xiě)出來(lái)的。所以說(shuō)在pygame框架里使用live2d就需要pygame的OPENGL模式以及l(fā)ive2d-py庫(kù)。所以本篇文章主要介紹1live2d-py庫(kù)的使用方法2OpenGL與pygame的交互已更新3部分OpenGL基礎(chǔ)知識(shí)目錄前言live2d-py①、導(dǎo)入模塊②、初始化③、實(shí)例化模型對(duì)象④、加載模型⑤、正常pygame循環(huán)⑥、其他常用方法1、Resize調(diào)整live2d大小2、SetScale設(shè)置模型縮放比例3、SetAutoBreathEnable開(kāi)啟/關(guān)閉 自動(dòng)呼吸4、SetAutoBlinkEnable開(kāi)啟/關(guān)閉 自動(dòng)眨眼5、SetOffset設(shè)置模型偏移量6、SetExpression設(shè)置live2d表情7、SetRandomExpression隨機(jī)設(shè)置一個(gè)live2d表情8、StartMotion設(shè)置live2d動(dòng)作9、StartRandomMotion隨機(jī)設(shè)置一個(gè)live2d動(dòng)作10、IsMotionFinished判斷當(dāng)前動(dòng)作是否完成11、ResetExpression重新設(shè)置live2d表情為默認(rèn)表情12、StopAllMotions停止所有的動(dòng)作⑦、其它注意事項(xiàng)1、禁止/允許日志輸出2、與pygame交互OpenGL Pygame交互①、安裝OpenGL②、渲染圖片新版1、導(dǎo)入圖片類2、使用圖片類3、放置舊版③、渲染文字④、注意1、多放置2、v2模型的特殊性3、V3模型的特殊性實(shí)例V2新版舊版實(shí)例V3新版舊版后記live2d-py首先肯定是需要pip一下pip install live2d-py之后我們需要知道對(duì)于live2d有v2與v3之分大致也就是2d與3d的區(qū)別。區(qū)別方式就是v2里面會(huì)有.moc文件而v3里面的是.moc3文件。①、導(dǎo)入模塊這時(shí)候就要根據(jù)我們的模型的版本進(jìn)行導(dǎo)入。如果是v2模型import live2d.v2 as live2d import pygame from pygame.locals import * #這個(gè)之后講暫時(shí)未用到 from OpenGL.GL import *反之是v3模型import live2d.v3 as live2d import pygame from pygame.locals import * #這個(gè)之后講暫時(shí)未用到 from OpenGL.GL import *不管是什么版本的模型之后的操作類似但不完全相同。②、初始化之后就是初始化#以pygame的交互為例 pygame.init() live2d.init() #但如果是對(duì)于v3模型需要加一句 if live2d.LIVE2D_VERSION 3: #這個(gè)函數(shù)會(huì)激活內(nèi)部的OpenGL函數(shù) #其實(shí)v2模型也能用這個(gè)函數(shù)但最好區(qū)分一下 live2d.glewInit() #這時(shí)候用到了pygame的OpenGL模式 pygame.display.set_mode((1200, 800), DOUBLEBUF | OPENGL, vsync 1)③、實(shí)例化模型對(duì)象之后就是需要實(shí)例化模型對(duì)象以此通過(guò)一些方法來(lái)控制live2d的行為。model live2d.LAppModel()之后就可以調(diào)用這個(gè)對(duì)象里的方法控制live2d。④、加載模型需要讓對(duì)象知道控制的哪個(gè)live2d#對(duì)于v2模型一般導(dǎo)入的是后綴名是(.model.json)的json文件路徑 #如 model_json_path: str my_v2_model.model.json #對(duì)于v3模型一般導(dǎo)入的是后綴名是(.model3.json)的json文件路徑 #如 model_json_path: str my_v3_model.model3.json #最后加載live2d model.LoadModelJson(model_json_path)⑤、正常pygame循環(huán)之后就可以通過(guò)pygame的事件來(lái)進(jìn)行一系列操作了。一般調(diào)用的順序就是clearBuffer - Update - Draw - flip/updaterun: bool True while run: for event in pygame.event.get(): match event.type: case pygame.QUIT: run not run break case _: pass #其他的操作之后就要刷新live2d了 ... #1、清除緩沖區(qū) live2d.clearBuffer() #2、更新live2d到緩沖區(qū) model.Update() #3、渲染live2d到屏幕 model.Draw() #4、需要用的pygame刷新 pygame.display.flip() #或者是pygame.display.update() #之后是其他的操作 ... #退出之后 #1、結(jié)束live2d live2d.dispose() #2、結(jié)束pygame pygame.quit() #3、結(jié)束程序 exit()這樣就能正常顯示live2d了。但是可能達(dá)不到自己想要的效果比如大小、位置、動(dòng)作、表情之類的還沒(méi)有設(shè)置那么請(qǐng)看下文所講述的。⑥、其他常用方法這個(gè)對(duì)象還有好多方法來(lái)控制live2d的一些屬性。1、Resize調(diào)整live2d大小window_width: int 800 window_height: int 600 model.Resize(window_width, window_height)2、SetScale設(shè)置模型縮放比例live2d_scale: float 0.8 #縮小了20%也就是變成了原來(lái)大小的80% model.SetScale(live2d_scale)3、SetAutoBreathEnable開(kāi)啟/關(guān)閉 自動(dòng)呼吸#開(kāi)啟自動(dòng)呼吸 model.SetAutoBreathEnable(True) #關(guān)閉自動(dòng)呼吸 model.SetAutoBreathEnable(False)4、SetAutoBlinkEnable開(kāi)啟/關(guān)閉 自動(dòng)眨眼#開(kāi)啟自動(dòng)眨眼 model.SetAutoBlinkEnable(True) #關(guān)閉自動(dòng)眨眼 model.SetAutoBlinkEnable(False)5、SetOffset設(shè)置模型偏移量offset_x: int 100 offset_y: int 100 #現(xiàn)在live2d會(huì)偏移原點(diǎn)(100, 100) model.SetOffset(offset_x, offset_y)6、SetExpression設(shè)置live2d表情#這個(gè)函數(shù)的第一個(gè)參數(shù)是字符串類型的表情ID #這個(gè)ID可以去相應(yīng)的.model.json里去查看 #進(jìn)入后找到expressions鍵里面的name都是表情ID #例如 model.SetExpression(cry) model.SetExpression(default)7、SetRandomExpression隨機(jī)設(shè)置一個(gè)live2d表情#單純的隨機(jī)挑選一個(gè)live2d表情 model.SetRandomExpression()8、StartMotion設(shè)置live2d動(dòng)作#這個(gè)函數(shù)有三個(gè)主要參數(shù)分別是 #group: str - 和SetExpression類似都是填入.model.json里面的name #no: int - 默認(rèn)為0就行 #priority: int - 代表這個(gè)動(dòng)作的優(yōu)先級(jí)畢竟動(dòng)作的發(fā)生需要時(shí)間沒(méi)有特殊需求那就1即可 #例如 model.StartMotion(happy, 0, 1)9、StartRandomMotion隨機(jī)設(shè)置一個(gè)live2d動(dòng)作#類似于SetRandonExpression model.StartRandomMotion()10、IsMotionFinished判斷當(dāng)前動(dòng)作是否完成#若當(dāng)前動(dòng)作完成 print(model.IsMotionFinished()) #輸出True #若當(dāng)前動(dòng)作未完成 print(model.IsMotionFinished()) #輸出False11、ResetExpression重新設(shè)置live2d表情為默認(rèn)表情#那什么是默認(rèn)的表情呢 #就是.model.json里面Expression里的default表情 model.ResetExpression()12、StopAllMotions停止所有的動(dòng)作model.StopAllMotions()⑦、其它注意事項(xiàng)1、禁止/允許日志輸出在正常情況下live2d-py這個(gè)庫(kù)會(huì)不斷地輸出日志到緩沖區(qū)可能會(huì)擾亂我們的正常工作。那么需要調(diào)用下列函數(shù)來(lái)“禁止日志輸出”import live2d.v2 as live2d #參數(shù)是False則禁止日志輸出 #反之允許日志輸出 live2d.setLogEnable(False)2、與pygame交互因?yàn)槭褂玫氖莗ygame的OpenGL模式所以pygame的圖片/文字渲染就行不通了現(xiàn)在需要使用OpenGL來(lái)進(jìn)行操作了。更新文章現(xiàn)有內(nèi)置圖片放置方法請(qǐng)直跳新版OpenGL Pygame交互我們需要知道OpenGL是一個(gè)狀態(tài)機(jī)就是通過(guò)改變?nèi)諳penGL的狀態(tài)來(lái)進(jìn)行差異化“輸出”。OpenGL主要由三個(gè)包構(gòu)成分別是-OpenGL.GL是核心API包含了主要函數(shù)-OpenGL.GLU包含了許多被包裝的實(shí)用方法-OpenGL.GLUT包含了關(guān)于屏幕的創(chuàng)建以及基本事件而我們?cè)谶@里只需要使用OpenGL.GL包就滿足了。①、安裝OpenGLpip install PyOpenGL PyOpenGL-accelerate②、渲染圖片現(xiàn)在live2d-py的最新版本“0.5.4”內(nèi)置了圖片放置方法比我之前自己琢磨的方法更加靠譜且快速請(qǐng)忽略舊版內(nèi)容直接學(xué)習(xí)新版方法即可。新版1、導(dǎo)入圖片類from live2d.utils.image import Image2、使用圖片類#這個(gè)函數(shù)就一個(gè)參數(shù)代表路徑 my_img: Image Image(你的圖片的路徑) #例如在相對(duì)路徑有個(gè)圖片叫“img.png” my_img: Image Image(img.png)3、放置#官方案例節(jié)選 ... running True while True: for event in pygame.event.get(): if event.type pygame.QUIT: running False break if event.type pygame.MOUSEMOTION: model.Drag(*pygame.mouse.get_pos()) if not running: break live2d.clearBuffer() #就在這里放置 my_img.Draw() model.Update() model.Draw() pygame.display.flip() ...舊版我們知道在pygame里都是以pygame.SurfaceType進(jìn)行渲染的而要在OpenGL的話需要將pygame.SurfaceType轉(zhuǎn)換成字節(jié)串bytes來(lái)轉(zhuǎn)換。需要注意的是OpenGL的坐標(biāo)系和pygame的坐標(biāo)系不一樣-OpenGL是以中心為原點(diǎn)的權(quán)值/±1坐標(biāo)系-pygame是以TopLeft為原點(diǎn)的數(shù)值坐標(biāo)系。如下圖具體方法講解如下#導(dǎo)入OpenGL.GL包 #下面沒(méi)見(jiàn)過(guò)的函數(shù)都是來(lái)自這里 from OpenGL.GL import * import pygame surface pygame.image.load(image.png).convert_alpha() #1、將surface對(duì)象轉(zhuǎn)成成字節(jié)串并保持RGB通道 texture_data: bytes pygame.image.tostring(surface, RGBA, 1) #2、獲得紋理id可以看成申請(qǐng)一段空間 id glGenTextures(1) #3、告訴OpenGL這段空間的紋理是2D紋理圖片 glBindTexture(GL_TEXTURE_2D, id) #4、將圖片的字節(jié)串綁定到這段空間里下面細(xì)講 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface.get_width(), surface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data ) #5、設(shè)置參數(shù)開(kāi)啟模糊但反鋸齒模式可以這么理解 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) #6、為了防止中間可能的其他函數(shù)的干擾需要再綁定一次 glBindTexture(GL_TEXTURE_2D, id) #7、渲染的開(kāi)始并且通過(guò)“四邊形”模式圖片渲染 glBegin(GL_QUADS) #8、如上圖 glTexCoord2f(0, 0); glVertex3f(x1, y1, z1) #如圖圖片左下角分別對(duì)應(yīng)xyz的權(quán)值z(mì)10即可 glTexCoord2f(1, 0); glVertex3f(x2, y2, z2) #如圖圖片右下角分別對(duì)應(yīng)xyz的權(quán)值z(mì)20即可 glTexCoord2f(1, 1); glVertex3f(x3, y3, z3) #如圖圖片右上角分別對(duì)應(yīng)xyz的權(quán)值z(mì)30即可 glTexCoord2f(0, 1); glVertex3f(x4, y4, z4) #如圖圖片左上角分別對(duì)應(yīng)xyz的權(quán)值z(mì)40即可 #9、結(jié)束渲染 glEnd()而這些函數(shù)需要在pygame基本循環(huán)里去使用就類似于pygame的blit一樣。當(dāng)然最好不要直接用最好把它們封裝成函數(shù)或類畢竟沒(méi)必要一直生成id直接一個(gè)變量保存下來(lái)就行。例如class Live2D(object): staticmethod def render(surface: pygame.SurfaceType) - object: texture_data: bytes pygame.image.tostring(surface, RGBA, 1) id: object glGenTextures(1) glBindTexture(GL_TEXTURE_2D, id) glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface.get_width(), surface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data ) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) return id staticmethod def blit(id: object, *args: tuple[tuple[3], tuple[3], tuple[3], tuple[3]]) - None: glBindTexture(GL_TEXTURE_2D, id) glBegin(GL_QUADS) glTexCoord2f(0, 0); glVertex3f(*args[3]) glTexCoord2f(1, 0); glVertex3f(*args[1]) glTexCoord2f(1, 1); glVertex3f(*args[2]) glTexCoord2f(0, 1); glVertex3f(*args[0]) glEnd()其中對(duì)于glTexImage2D函數(shù)C解釋來(lái)自微軟void WINAPI glTexImage2D( GLenum target, #模式GL_TEXTURE_2D GLint level, #細(xì)節(jié)級(jí)別編號(hào)。 級(jí)別 0 是基礎(chǔ)圖像級(jí)別。 GLint internalformat, #紋理中顏色分量的數(shù)量與tostring的相同,GL_RGBA GLsizei width, #長(zhǎng)度 GLsizei height, #寬度 GLint border, #邊框的寬度必須為 0 或 1為1就行。 GLint format, #和level一樣都選GL_RGBA GLenum type, #字節(jié)串類型bytes const GLvoid *pixels #紋理數(shù)據(jù)就是那個(gè)字節(jié)串 );③、渲染文字和渲染圖片類似需要注意的是back_color (255, 255, 255) font_color (0, 0, 0) text_surface pygame.font.SysFont(幼圓, 36).render(Hello, True, font_color, back_color) #注意這里的back_color不能省略了如果省略那么文字不能正常顯示了④、注意新版無(wú)需閱讀此處一般以上方法都是可以使用的但真正實(shí)踐的時(shí)候有復(fù)雜需求的話會(huì)出現(xiàn)諸多問(wèn)題有些我能解決但有些解決不了1、多放置新版已解決不允許多放置放置多個(gè)時(shí)放置的不知道為什么全是第一個(gè)。局限在v2模型2、v2模型的特殊性僅僅在v2模型的展示時(shí)需要在最后加一句glUseProgram(0)來(lái)恢復(fù)設(shè)置才能正常的放置圖片/文字并顯示run: bool True while run: #其他的操作之后就要刷新live2d了 ... #1、清除緩沖區(qū) live2d.clearBuffer() #2、加載圖片 Live2D.blit(back, *back_pos) #3、更新live2d到緩沖區(qū) model.Update() #4、渲染live2d到屏幕 model.Draw() #5、需要用的pygame刷新 pygame.display.flip() #或者是pygame.display.update() #之后是其他的操作 ... #?、就是這句話 glUseProgram(0) #退出之后 #1、結(jié)束live2d live2d.dispose() #2、結(jié)束pygame pygame.quit() #3、結(jié)束程序 exit()3、V3模型的特殊性僅僅在v3模型的展示時(shí)需要在加載圖片前加一句glEnable(GL_TEXTURE_2D)才能正常的放置圖片/文字并顯示run: bool True while run: #其他的操作之后就要刷新live2d了 ... #1、清除緩沖區(qū) live2d.clearBuffer() #?、就是這句話 glEnable(GL_TEXTURE_2D) #2、加載圖片 Live2D.blit(back, *back_pos) #3、更新live2d到緩沖區(qū) model.Update() #4、渲染live2d到屏幕 model.Draw() #5、需要用的pygame刷新 pygame.display.flip() #或者是pygame.display.update() #之后是其他的操作 ... #而這句話不能有了 #glUseProgram(0) #退出之后 #1、結(jié)束live2d live2d.dispose() #2、結(jié)束pygame pygame.quit() #3、結(jié)束程序 exit()?如果大家能解決的話一定要發(fā)出來(lái)造福大家啊實(shí)例V2最后就舉個(gè)完整的實(shí)例來(lái)結(jié)束文章下面的全部上面都出現(xiàn)過(guò)新版# -*- coding: utf-8 -*- # -*- file: live2d_test.py -*- import pygame import live2d.v2 as live2d from pygame.locals import * from live2d.utils.image import Image #導(dǎo)入新版的圖片模塊 live2d.setLogEnable(False) pygame.init() live2d.init() pygame.display.set_mode((1200, 800), DOUBLEBUF | OPENGL, vsync 1) model: live2d.LAppModel live2d.LAppModel() model.LoadModelJson(rv2\kasumi2\kasumi2.model.json) model.Resize(1200, 800) model.SetAutoBlinkEnable(True) model.SetAutoBreathEnable(True) model.StartRandomMotion() #創(chuàng)建圖片對(duì)象 back: Image Image(beach1.jpg) run: bool True while run: for event in pygame.event.get(): if event.type pygame.QUIT: run not run break live2d.clearBuffer() #放置圖片 back.Draw() model.Update() model.Draw() pygame.display.flip() live2d.dispose() pygame.quit() exit()舊版# -*- coding: utf-8 -*- # -*- file: live2d_test.py -*- import live2d.v2 as live2d import pygame from pygame.locals import * from OpenGL.GL import * class Live2D(object): staticmethod def render(surface: pygame.SurfaceType) - object: texture_data pygame.image.tostring(surface, RGBA, 1) id: object glGenTextures(1) glBindTexture(GL_TEXTURE_2D, id) glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface.get_width(), surface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data ) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) return id staticmethod def blit(id: object, *args: tuple[tuple[3], tuple[3], tuple[3], tuple[3]]) - None: glBindTexture(GL_TEXTURE_2D, id) glBegin(GL_QUADS) glTexCoord2f(0, 0); glVertex3f(*args[3]) glTexCoord2f(1, 0); glVertex3f(*args[1]) glTexCoord2f(1, 1); glVertex3f(*args[2]) glTexCoord2f(0, 1); glVertex3f(*args[0]) glEnd() live2d.setLogEnable(False) pygame.init() live2d.init() pygame.display.set_mode((1200, 800), DOUBLEBUF | OPENGL, vsync 1) model: live2d.LAppModel live2d.LAppModel() model.LoadModelJson(rv2\kasumi2\kasumi2.model.json) model.Resize(1200, 800) model.SetAutoBlinkEnable(True) model.SetAutoBreathEnable(True) model.StartRandomMotion() back Live2D.render(pygame.image.load(rbeach1.jpg).convert_alpha()) back_pos ((-1.0, 1.0, 0), (1.0, -1.0, 0), (1.0, 1.0, 0), (-1.0, -1.0, 0)) run: bool True while run: for event in pygame.event.get(): if event.type pygame.QUIT: run not run break live2d.clearBuffer() Live2D.blit(back, *back_pos) model.Update() model.Draw() pygame.display.flip() if live2d.LIVE2D_VERSION 2: glUseProgram(0) live2d.dispose() pygame.quit() exit()實(shí)例V3新版# -*- coding: utf-8 -*- # -*- file: live2d_test.py -*- import pygame import live2d.v3 as live2d from pygame.locals import * from live2d.utils.image import Image #導(dǎo)入新版的圖片模塊 live2d.setLogEnable(False) pygame.init() live2d.init() pygame.display.set_mode((1200, 800), DOUBLEBUF | OPENGL, vsync 1) if live2d.LIVE2D_VERSION 3: #注意此處最好使用新版的live2d.glInit()來(lái)代替舊版的live2d.glewInit() live2d.glInit() model: live2d.LAppModel live2d.LAppModel() model.LoadModelJson(rv3\Mao\Mao.model3.json) model.Resize(1200, 800) model.SetAutoBlinkEnable(True) model.SetAutoBreathEnable(True) model.StartRandomMotion() #創(chuàng)建圖片對(duì)象 back: Image Image(beach1.jpg) run: bool True while run: for event in pygame.event.get(): if event.type pygame.QUIT: run not run break live2d.clearBuffer() #放置圖片 back.Draw() model.Update() model.Draw() pygame.display.flip() live2d.dispose() pygame.quit() exit()舊版# -*- coding: utf-8 -*- # -*- file: live2d_test.py -*- import live2d.v3 as live2d import pygame from pygame.locals import * from OpenGL.GL import * class Live2D(object): staticmethod def render(surface: pygame.SurfaceType) - object: texture_data pygame.image.tostring(surface, RGBA, 1) id: object glGenTextures(1) glBindTexture(GL_TEXTURE_2D, id) glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface.get_width(), surface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data ) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) return id staticmethod def blit(id: object, *args: tuple[tuple[3], tuple[3], tuple[3], tuple[3]]) - None: glBindTexture(GL_TEXTURE_2D, id) glBegin(GL_QUADS) glTexCoord2f(0, 0); glVertex3f(*args[3]) glTexCoord2f(1, 0); glVertex3f(*args[1]) glTexCoord2f(1, 1); glVertex3f(*args[2]) glTexCoord2f(0, 1); glVertex3f(*args[0]) glEnd() live2d.setLogEnable(False) pygame.init() live2d.init() pygame.display.set_mode((1200, 800), DOUBLEBUF | OPENGL, vsync 1) if live2d.LIVE2D_VERSION 3: live2d.glewInit() model: live2d.LAppModel live2d.LAppModel() model.LoadModelJson(rResources\v3\Mao\Mao.model3.json) model.Resize(1200, 800) model.SetAutoBlinkEnable(True) model.SetAutoBreathEnable(True) model.StartRandomMotion() back Live2D.render(pygame.image.load(rbeach1.jpg).convert_alpha()) back_pos ((-1.0, 1.0, 0), (1.0, -1.0, 0), (1.0, 1.0, 0), (-1.0, -1.0, 0)) run: bool True while run: for event in pygame.event.get(): if event.type pygame.QUIT: run not run break live2d.clearBuffer() glEnable(GL_TEXTURE_2D) Live2D.blit(back, *back_pos) model.Update() model.Draw() pygame.display.flip() live2d.dispose() pygame.quit() exit()后記文章有問(wèn)題還請(qǐng)大家指正。live2d-py項(xiàng)目是國(guó)人發(fā)起的作者在BiliBili上有賬號(hào)。文章更新時(shí)間2025.10.01kirakiradokidoki?~