kimi2.5到目前为止没有找到好的白嫖方法(
我这两天在研究某个东西,昨天一天花了40块钱…全都用来ai生图了
一个月烧400,这AI是用电还是用钱啊?
硅基流动这是把你当矿机了吧
其实是我把硅基流动当矿机了(
看似花了400+其实没冲一分钱
好家伙,原来你才是矿工,硅基流动是矿场啊
白嫖的快乐我懂了
所以quiz你毕业了之后打算怎么办,白嫖能一直持续下去吗
同问,白嫖党毕业后是不是就得自己养AI了?还是说能找到新的白嫖圣地?![]()
不到啊,hws一直想研究接手,但是到现在他还没有搞明白怎么部署
我有一个想法,做一个游戏,但这个游戏什么都没有,全靠AI生成,包括剧情、NPC及人设、美术等等。我看到 Genie 3 — Google DeepMind 但这个感觉比较侧重世界,它生成的游戏没有什么剧情什么的。
至于为什么想做这个东西呢,因为高强度玩了几天终末地突发奇想,通过这种方式,理论上可以得到一个,剧情无限长、有无限种选择、个性化的游戏。目前一些单机游戏貌似会给「不同的结局」但这种游戏剧情都不算太长。而剧情很长的游戏,基本上不会给你可选择的剧情,有选项也是殊途同归。而这个AI游戏,可以基于玩家和NPC的对话什么的,导向完全不同的未来,并且剧情可以一直延伸。我希望能实现从美术风格、战斗、玩法等几乎所有东西都AI制作,从而完全个性化
目测的缺点有,AI做出来的东西不稳定,可能有很多问题,剧情深度什么的可能和人做的有很大差距等等。
我目前准备从2D入手(因为3D比较麻烦),我实现一个类似游戏引擎的东西,然后给出一些接口。LLM可以编写脚本来呈现出游戏场景,包括放置NPC什么的。
大概流程是LLM先思考和设计长的剧情安排,再将每一幕剧情通过编写游戏脚本呈现给玩家。其中所有可对话NPC也是LLM。所有NPC、物品等的贴图由LLM描述,调用图片生成模型生成。
目前我的阶段性成果是,我已经实现了「游戏引擎」和暴露给LLM的接口,给LLM的游戏脚本语言选了Js。
我实现的,给AI的接口(其实是AI的提示词)
// 以下是游戏脚本的类型定义,供脚本编写时参考使用
// 注意虽然定义以TypeScript形式给出,但这只是为了说明类型,你编写的脚本仍应该使用javascript语法,而不是TypeScript语法
interface Position
{
getX(): number;
getY(): number;
}
interface Player
{
// 攻击力
getDamage(): number;
setDamage(damage: number): void;
// 当前血量(生命值)
getHealth(): number;
setHealth(health: number): void;
// 最大血量(生命值)
getMaxHealth(): number;
setMaxHealth(maxHealth: number): void;
// 移动速度
getSpeed(): number;
setSpeed(speed: number): void;
// 像玩家展示一段文字
showMessage(message: string): void;
}
interface Item
{
// 物品名称(将显示给玩家)
name: string;
// 物品的外观
// 该外观应当是一个图片的id,图片可以通过resources.generateImage生成
imageId: string;
// 效果,**该效果会在物品被玩家装备后游戏每一帧调用一次**(游戏为每秒20帧)
// env: 当前环境对象
// player: 当前玩家对象
// breakItem: 让当前物品被移除(销毁)
// state: 物品的状态对象,可用于存储物品的状态数据
// 该函数需要返回更新后的状态对象
// 注意,玩家的攻击力、血量上限、移动速度这3个属性会在每一帧结束时重置为基础值(即玩家未装备任何物品时的属性值),
// 因此如果你想通过物品来提升这些属性,需要在effect中每一帧都重新设置这些属性。
// 而血量不会重置,因此请注意,如果你的物品时一次性的,例如治疗,你要避免每一帧都增加玩家的血量。
// 而如果你的物品是持续性的,例如增加移动速度,你需要每一帧都设置玩家的移动速度。
effect: (game: Game, breakItem: () => void, state: any) => any;
// 物品描述(将显示给玩家)
description: (state: any) => string;
// 物品标签,这不会显示给玩家,但你在脚本中可以用来区分物品类型等用途
tags: string[];
// 物品是否可以装备
// currentEquippedItems: 当前玩家已经装备的物品列表
equippable: (currentEquippedItems: Item[]) => boolean;
}
// 一个地图元素
interface Element
{
// 元素的外观
// 该外观应当是一个图片的id,图片可以通过resources.generateImage生成
imageId: string;
// 在地图上占据的宽和高,必须是正整数
sizeX: number;
sizeY: number;
// 该元素是否可以通过(即玩家能否走过该元素)
passable: boolean;
// 若passable为true,则当玩家站在该元素上的时候会触发的效果
onStep: ((game: Game, storyState: any) => any) | null;
// 当玩家尝试和该元素交互的时候触发的效果,如果为null则表示该元素不可交互
// 注意,一个onInteract不是null的元素,passable必须是true. 因为玩家必须走到元素上才能和他交互
// 另外,在绘制地图和放置NPC的时候请务必注意,不要把可交互NPC(非敌人NPC)放在可交互元素上,否则会导致玩家无法和NPC交互(因为玩家优先和元素交互)
onInteract: ((game: Game, storyState: any) => any) | null;
}
interface Environment
{
getPlayer(): Player;
getPlayerInventory(): string[];
getPlayerPosition(): Position;
giveItem(item: Item, initialState: any): void;
movePlayer(x: number, y: number): void;
}
interface Resources
{
// 调用图片生成模型,生成一张图片。图片可用于npc的外观等。
generateImage(
id: string,
prompt: string
): void;
getImages(): string[];
// 创建一个长期npc。即会多次出现的npc。
createNPC(
id: string,
name: string,
// npc的外观图片,使用generateImage生成的图片id。
imageId: string,
// npc的身份设定,应当是一个详尽的描述,包含性格、背景故事等。
// 用第二人称描述,例如:“你是一个……” 该描述将直接给AI,以让其扮演NPC,因此应当详尽。
personality: string
): void;
getNPCs(): string[];
// 创建一个物品,方便之后使用,来避免重复创建相同物品。
createItem(
id: string,
item: Item
): void;
getItem(
id: string
): Item | null;
getItems(): string[];
// 创建一个地图元素,方便之后使用,来避免重复创建相同元素。
createElement(id: string, element: Element): void;
getElement(id: string): Element | null;
getElements(): string[];
}
interface Game
{
resources: Resources;
env: Environment;
// 开始游戏,让玩家进入某个故事场景
playStory(story: Story): void;
}
interface Story
{
// 地图的背景图片,使用generateImage生成的图片id。
// 由于这是一个2d游戏,因此这个图片应当是一个俯视的地面的图片。
backgroundImageId: string;
// 初始故事状态
initialState: any;
// 绘制地图的方法,在该方法中调用setElement来设置地图上的元素。
// 该方法会在游戏开始时调用一次,用于生成地图。
// setElement: 用于设置地图上的元素,x和y为元素的左上角坐标(应当是正整数),element为要设置的元素。
// 请注意,地图坐标系的原点(0,0)在左上角,x轴向右延伸,y轴向下延伸。并且,如果element发生重叠,后设置的元素会覆盖先设置的元素。
drawMap: (game: Game, setElement: (x: number, y: number, element: Element) => void) => void;
// 该方法会在游戏开始时、drawMap之后调用一次,用于放置NPC。
// placeNPC: 用于放置NPC,x和y为NPC的坐标(可以是小数),npc为要放置的NPC,见ActiveNPC接口。
putNPCs: (game: Game, placeNPC: (x: number, y: number, npc: ActiveNPC) => void) => void;
// 初始玩家位置
initialPlayerX: number;
initialPlayerY: number;
// 另外注意 玩家和npc的碰撞箱都是0.5x0.5的正方形,且位置是以它们的中心点为准的。npc和玩家之间、npc和npc之间、npc和非passable的元素之间、玩家和非passable的元素之间,都**不能**重叠。否则story将创建报错。
// 故事(任务)标题
storyTitle: string;
// 故事(任务)说明,例如玩家需要击败xxx,和xxx对话等
storyDescription: string;
// 什么时候故事(任务)完成
// game: 当前游戏对象
// state: 当前的story状态对象
isStoryFinished: (game: Game, state: any) => boolean;
}
interface ActiveNPC
{
// npc基础信息,如果是一次性的npc(只在当前story出现一次的临时npc)则传入一个对象
// 否则传入一个字符串,表示之前通过resources.createNPC创建的npc的id。
npc: string | {
id: string;
name: string;
imageId: string;
personality: string;
};
// 用第二人称,告诉AI当下的状态等,以及他需要和玩家说什么做什么。
// 和personality不同,personality是npc的人格设定,而messageToNPC是当前场景下,npc需要知道的内容。
// 注意用resources.createNPC创建NPC是由长期记忆的,即NPC会记住它上次出现在某个story中的时候,你告诉了他什么,以及他和玩家发生了哪些互动。
// 这个messageToNPC你应当告诉当前处于什么场景,一些他应当知道的信息和剧情,如果之前出现过该npc,则建议你告知他上次互动后又发生了些什么等等
// 总之,messageToNPC应当让NPC清楚当前的剧情背景,以及他现在应该做什么说什么。
messageToNPC: string;
// npc可以调用的工具
tools: {
id: string;
// 工具的描述,告诉NPC这个工具是干什么用的,如何使用它。
description: string;
// game: 当前游戏对象
// npc: 当前NPC对象
// arg: NPC调用该工具时传入的参数
// state: 当前的story状态对象
// 返回一个msg告诉npc,例如调用成功。newState为更新后的story状态对象
onCall: (game: Game, arg: any, storyState: any) => { msg: string; newState: any }
}[];
// 敌人,如果传递了该属性,则此NPC将变为敌人,会主动攻击玩家,并且玩家可以攻击他
// 如果传递了该属性,NPC将变为敌人NPC,主动攻击玩家,直到被玩家击败。他将无法再和玩家对话,因此tools将被忽略
// 对于一个敌人NPC 如果他是一个长期NPC(通过resources.createNPC创建的),则messageToNPC会被压入他的记忆中,你可以在messageToNPC中写例如「你和玩家打了一架」等内容,以让他记住之前和玩家的战斗过。如果不是长期NPC,则messageToNPC将没有任何作用
enemyAttributes?: {
// 敌人的初始血量
health: number;
// 敌人的攻击力
damage: number;
// 敌人被击败时触发的效果
onDefeated: (game: Game, storyState: any) => any;
// 需要注意,玩家发射的子弹每次命中敌人,会造成等同于玩家攻击力的伤害。因此,假设你将敌人的血量设为玩家攻击力的10倍,则玩家需要命中敌人10次才能击败他。
}
}
declare const game: Game; // game是系统定义的全局变量,在你的代码中直接使用
/*
一个简单的示例脚本,他将创建一个这样的地图:
####################
#..................#
#..................#
#..................#
#.......CC.....N...#
#.......CC.........#
#..................#
#..................#
#..................#
####################
####################
#..................#
#..................#
#..................#
#.......AA.....BB..#
#.......AA.....BB..#
#..................#
#..................#
#P.................#
####################
其中,P是玩家初始位置,A是一个治疗泉水,每0.5秒治疗玩家1点血量,B是一个传送门,将玩家传送到上方区域
C是一个宝箱,打开后赠送玩家一个可以增加3点血量上限,并且可以复活一次玩家,但在复活后销毁的护身符
N是一个NPC,他的任务是询问玩家的名字,并在得到玩家的名字后,结束当前任务。
```javascript
// 创建图片:草地
game.resources.generateImage("grass", "A top-down view of a grassy field, bright and vibrant colors, high detail");
// 创建图片:宝箱
game.resources.generateImage("chest", "A view of a wooden treasure chest with metal reinforcements, slightly open, high detail");
// 创建图片:治疗泉水
game.resources.generateImage("healing_spring", "A magical healing spring with sparkling water, surrounded by glowing plants, high detail");
// 创建图片:传送门
game.resources.generateImage("portal", "A mystical glowing portal, swirling with magical energy, high detail");
// 创建图片:护身符
game.resources.generateImage("amulet", "A golden amulet with intricate engravings, glowing faintly, high detail");
// 创建图片:NPC外观
game.resources.generateImage("npc_image", "A friendly looking character with a warm smile, high detail");
// 注意这只是简单示例,实际上生成图片的提示词需要十分详尽,以确保生成的图片符合预期。而不是现在这样简单的一句话。
// 创建地图元素:边界墙
game.resources.generateImage("wall", "A top-down view of a stone wall, gray stones with moss, high detail");
game.resources.createElement("wall", {
imageId: "wall",
sizeX: 1,
sizeY: 1,
passable: false,
onStep: null
onInteract: null,
});
game.playStory({
backgroundImageId: "grass", // 使用刚刚创建的草地图片作为背景
initialState: {
playerName: null,
chestOpened: false,
frameCount: 0,
finished: false,
},
storyTitle: "初来乍到",
storyDescription: "与陌生人交谈",
isStoryFinished: (game, state) => {
return state.playerName !== null && state.finished;
},
// 左上角是(0,0),因此玩家的初始位置是(1.5, 18.5)这样玩家会位于上面图中最左下角的格子的中央
initialPlayerX: 1.5,
initialPlayerY: 18.5,
drawMap: (game, setElement) => {
// 绘制墙
for (let x = 0; x < 20; x++) {
// 最上面和最下面的墙
setElement(x, 0, game.resources.getElement("wall"));
setElement(x, 19, game.resources.getElement("wall"));
// 中间的隔断
setElement(x, 9, game.resources.getElement("wall"));
setElement(x, 10, game.resources.getElement("wall"));
}
for (let y = 0; y < 20; y++) {
// 左右两侧的墙
setElement(0, y, game.resources.getElement("wall"));
setElement(19, y, game.resources.getElement("wall"));
}
// 绘制宝箱
setElement(8, 4, {
imageId: "chest",
sizeX: 2,
sizeY: 2,
passable: true,
onStep: null,
onInteract: (game, state) => {
if (state.chestOpened) {
game.env.getPlayer().showMessage("宝箱已经被打开了。");
return state;
}
state.chestOpened = true;
game.env.getPlayer().showMessage("你打开了宝箱,获得了一个护身符!");
game.env.giveItem({
name: "护身符",
imageId: "amulet",
effect: (game, breakItem, state) => {
const player = game.env.getPlayer();
// 每帧运行,增加3点最大生命值
player.setMaxHealth(player.getMaxHealth() + 3);
// 如果玩家死亡,则复活,然后销毁该护身符
if (player.getHealth() <= 0) {
player.setHealth(player.getMaxHealth());
game.env.getPlayer().showMessage("护身符复活了你!");
breakItem();
}
return state;
},
description: (state) => "一个神秘的护身符,可以增加3点最大生命值,并且在你死亡时复活你一次。",
tags: ["amulet", "revive"],
// 当装备其他护身符时不可再装备此护身符
equippable: (currentEquippedItems) => {
return !currentEquippedItems.some(item => item.tags.includes("amulet"));
},
}, {});
return state;
},
});
// 绘制治疗区域
setElement(8, 14, {
imageId: "healing_spring",
sizeX: 2,
sizeY: 2,
passable: true,
onStep: (game, state) => {
const player = game.env.getPlayer();
// 每0.5秒治疗1点血量(注意游戏每秒20帧,因此每10帧治疗1点血量)
state.frameCount++;
state.frameCount %= 10;
if (state.frameCount === 0) {
if (player.getHealth() < player.getMaxHealth()) {
player.setHealth(player.getHealth() + 1);
}
}
return state;
},
onInteract: null,
});
// 绘制传送门
setElement(15, 14, {
imageId: "portal",
sizeX: 2,
sizeY: 2,
passable: true,
onStep: (game, state) => {
// 传送玩家到左上角(1.5, 1.5)
game.env.movePlayer(1.5, 1.5)
return state;
},
onInteract: null,
});
},
putNPCs: (game, placeNPC) => {
placeNPC(15.5, 4.5, {
npc: {
id: "friendly_npc",
name: "友好的陌生人",
imageId: "npc_image",
personality: "这是一个xxxxxx世界,你是这个世界中的一个路人,你性格友好,乐于助人,喜欢和别人交朋友。", // 一般来说,你需要告知世界观等,以让NPC更好地融入世界,以及当玩家问及世界信息的时候NPC能给出合理,并且正确的回答。
},
messageToNPC: "你现在在树林里散步,遇到了玩家,你的任务是询问玩家的名字,在得到玩家的名字后,调用name工具,告诉我玩家的名字。然后询问玩家是否还有其他问题,当玩家确认没有问题后,调用finish工具,结束当前任务。",
tools: [
{
id: "name",
description: "询问玩家的名字后,调用这个工具,传入一个对象: { playerName: string }。",
onCall: (game, arg, storyState) => {
return {
msg: `玩家名字已经被记录为 ${arg.playerName}。`,
newState: { ...storyState, playerName: arg.playerName },
};
},
},
{
id: "finish",
description: "当玩家确认没有其他问题后,调用这个工具,结束当前任务。",
onCall: (game, arg, storyState) => {
if (!storyState.playerName) {
return {
msg: "你还没有得到玩家的名字,不能结束任务。请先询问玩家的名字。",
newState: storyState,
};
}
return {
msg: "任务完成",
newState: { ...storyState, finished: true },
};
},
},
],
});
},
});
以上是一个完整的示例脚本,展示了如何使用js来向玩家呈现游戏。
在这个脚本中,一次性完成了从图片生成到最终呈现故事,但实际上,你可以拆分多次,而不必要一次性完成所有内容。拆分多次可以更好的控制,避免中途失败导致全都作废。
例如:
第一次:game.resources.generateImage(“grass”, “A top-down view of a grassy field, bright and vibrant colors, high detail”);
第二次:game.resources.createElement(“wall”, { imageId: “wall”, sizeX: 1, sizeY: 1, passable: false, onStep: null, onInteract: null, });
…
第三次:game.playStory({ … });
如果你需要获取一些信息,例如获取当前的npc列表,你可以使用console.log,若控制台有输出,则将返回给你
例如: console.log(game.resources.getNPCs());
另外,当调用game.playStory后,程序将进入游戏状态,直到目标完成后,才会将最终的state返回给你。因此在上面的示例中,你调用完game.playStory后,玩家将进入游戏,最终在任务完成后,程序会将最终的state返回给你。如:
{
playerName: "XXXX",
chestOpened: true,
frameCount: 7,
finished: true,
}
在剧情中,你可以多给出一些分支,例如让NPC询问玩家愿意做A还是B。或者直接弄两个NPC,让玩家选择和哪个NPC对话等。
之后将这些信息存储在state中,你再通过state来决定后续剧情的发展。
另外需要特别注意:
- 你编写的所有函数(包括effect,onStep,onInteract,onCall等)都不得使用任何外部的变量或函数,仅能使用传入的参数和内部变量,否则将会报错!这是为了保证脚本的安全性,防止脚本执行时访问外部环境。
例如:
let x = 10;
setElement(..., {
...
onStep: (game, state) => {
x // 报错!!!
return state;
},
...
});
2. 所有的必须使用箭头函数(即 ()=>{} 这种)禁用function关键字定义的函数
3. 所有的state对象(包括story state,item state等)都必须可以被JSON.stringify序列化并通过JSON.parse恢复。否则会在需要存储state时出错。例如,state中不能包含函数,不能包含循环引用等。
4. 上述API可以做出很多效果,例如没隔固定时间给玩家回血,让NPC在听到玩家的特定回答后给玩家东西,解密获得答案告诉NPC触发后续剧情等。请善用这些API来丰富你的游戏内容。
*/
当前的给AI的自由度还是不够高,像是战斗系统什么的,基本上AI只有权改个参数,并不能自定义战斗的操作什么的,距离我希望的完全AI还非常遥远。现在我这套设计基本是「可用的」,AI可以做出游戏 让ds设计剧情和编写脚本 。然而ai写出的剧情貌似还不太行。Ai生成的图片美术风格上也很难美观、统一
我目前在考虑拆分游戏脚本编写和剧情设计,变成一个agent设计剧情,再把剧情交由专门编写游戏脚本的agent,编写游戏脚本呈现给玩家。这样可以让负责剧情的AI不被干扰,能一定程度上提高一点剧情质量。
好家伙,你这是要创造元宇宙啊?无限剧情+全AI生成,这要成了游戏界得地震。不过AI生成的剧情深度和美术统一性确实是个大坑,期待你的阶段性成果!
o 还有一个问题是天价花费,一张图片生成几毛钱,LLM的token的钱,基本上这个游戏跑起来就是在烧钱
所以这游戏不光烧显卡,还烧钱包啊?真正的“氪金”游戏了属于是。感觉可以加个“一键贷款”功能了

更新系统看到这个…还差个antigravity就齐了
是的
抱歉有段时间没上论坛
主要是感觉现在gpt回复没有思考过程,略难受
现在问题是,我的gpt是白嫖的 poe.com 并非官方。他那个根本不提供response的接口。所以增加了response接口的支持现在基本上没有任何用,只能闲置
现在gpt好像会输出思考啊?虽然我没用过官网gpt并不知道这是不是真正的思考过程![]()
可以搞个codex上去
为什么麦当劳和肯德基在早上都没有正餐。我非常需要在早上吃晚餐
试试汉堡王吗
比较接近正餐形式的早餐
