会员登录 立即注册

搜索

训练神经网络自动玩游戏

[复制链接]
崎山小鹿 发表于 2024-10-21 12:32:57 | 显示全部楼层 |阅读模式
崎山小鹿
2024-10-21 12:32:57 2906 5 看全部
用神经网络玩游戏
CartPole是OpenAI gym中的一个游戏测试,车上顶着一个自由摆动的杆子,实现杆子的平衡,杆子每次倒向一端车就开始移动让杆子保持动态直立的状态.
cart_pole.gif
游戏地址:https://gymnasium.farama.org/env ... _control/cart_pole/

一、搭建游戏运行环境
pip install swig
pip install gymnasium[box2d]
微信图片_20241021130431.png

CartPole 环境内置在 gym 中,直接安装 gym 即可。其环境 id 是CartPole-v0 。Gym是一个研究和开发强化学习相关算法的仿真平台。简单来说OpenAI Gym提供了许多问题和环境(或游戏)的接口,而用户无需过多了解游戏的内部实现,通过简单地调用就可以用来测试和仿真。
pip install gym
微信图片_20241021133237.png

启动游戏完整代码:
import gym

# 创建 CartPole 环境
env = gym.make('CartPole-v1', render_mode="human")  # 使用新版本时,需要指定 render_mode

# 重置环境,准备开始游戏
observation, info = env.reset()

for _ in range(1000):
    # 随机选择一个动作(0或1)
    action = env.action_space.sample()

    # 应用动作到环境中,返回新的状态、奖励、完成标志和其他信息
    observation, reward, done, truncated, info = env.step(action)

    # 如果游戏结束或被截断,重置环境
    if done or truncated:
        observation, info = env.reset()

# 关闭游戏
env.close()
可以看到游戏在自动运行。

二、手动控制
增加键盘操作:

# 获取键盘按键状态
keys = pygame.key.get_pressed()

# 默认动作为随机生成 0 或 1,除非检测到按键输入
if not keys[K_LEFT] and not keys[K_RIGHT]:
    action = random.choice([0, 1])  # 随机选择 0 或 1

# 根据按键改变动作
if keys[K_LEFT]:
    print("Left arrow pressed")
    action = 0  # 向左移动
elif keys[K_RIGHT]:
    print("Right arrow pressed")
    action = 1  # 向右移动



完整代码如下:
import gym
import pygame
from pygame.locals import K_LEFT, K_RIGHT, QUIT
import time
import random  # 用于生成随机数

# 初始化 pygame
pygame.init()

# 设置帧率
FPS = 60
clock = pygame.time.Clock()

# 创建 CartPole 环境,使用 Gym 自带的渲染模式
env = gym.make('CartPole-v1', render_mode="human")

# 重置环境,准备开始游戏
observation, info = env.reset()

# 初始化游戏的运行标志
running = True

# 操作次数
action_count = 0

# 记录游戏开始的时间
start_time = time.time()

# 游戏循环
while running:
    # 处理事件队列,检测关闭窗口操作
    for event in pygame.event.get():
        if event.type == QUIT:  # 退出事件
            running = False
    time.sleep(0.3)

    # 获取键盘按键状态
    keys = pygame.key.get_pressed()

    # 默认动作为随机生成 0 或 1,除非检测到按键输入
    if not keys[K_LEFT] and not keys[K_RIGHT]:
        action = random.choice([0, 1])  # 随机选择 0 或 1

    # 根据按键改变动作
    if keys[K_LEFT]:
        print("Left arrow pressed")
        action = 0  # 向左移动
    elif keys[K_RIGHT]:
        print("Right arrow pressed")
        action = 1  # 向右移动

    # 执行动作并更新环境状态
    observation, reward, done, truncated, info = env.step(action)

    # 每执行一次动作,增加操作次数
    action_count += 1

    # 渲染环境,减少渲染频率(每隔 10 帧渲染一次)
    if action_count % 10 == 0:
        env.render()

    # 检查游戏是否结束
    if done or truncated:
        # 记录游戏结束的时间
        end_time = time.time()

        # 计算游戏持续的时间(秒)
        elapsed_time = end_time - start_time

        # 打印游戏信息
        print(f"游戏结束!总共操作了 {action_count} 次,持续时间为 {elapsed_time:.2f} 秒")

        # 重置环境
        observation, info = env.reset()

        # 重置计数器和时间
        action_count = 0
        start_time = time.time()

    # 控制帧率,确保游戏速度合适
    clock.tick(FPS)  # 设置帧率为 60 帧/秒

# 关闭游戏环境和 pygame
env.close()
pygame.quit()


最好成绩成功控制了57次。
微信图片_20241021170533.png


三、用电脑控制
#电脑自嗨
if observation[2] <= 0:
    action = 0
else:
    action = 1


电脑居然控制了59步。

四、用神经网络控制

1.训练神经网络

pip install torch

import gym
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import random
from collections import deque

# 设置设备(如果有GPU则使用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 创建 CartPole 环境
env = gym.make('CartPole-v1')

# 超参数
GAMMA = 0.99  # 折扣因子
EPSILON_START = 1.0  # 初始探索率
EPSILON_END = 0.01  # 最低探索率
EPSILON_DECAY = 0.995  # 探索率衰减
LEARNING_RATE = 0.001  # 学习率
MEMORY_SIZE = 10000  # 经验回放的容量
BATCH_SIZE = 64  # 批量大小
TARGET_UPDATE = 10  # 每隔多少步更新目标网络
TRAIN_TARGET_REWARD = 5000  # 训练停止目标:游戏持续达到 5000 步

# DQN 网络结构
class DQN(nn.Module):
    def __init__(self, state_size, action_size):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(state_size, 24)
        self.fc2 = nn.Linear(24, 24)
        self.fc3 = nn.Linear(24, action_size)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)

# 经验回放池
class ReplayMemory:
    def __init__(self, capacity):
        self.memory = deque(maxlen=capacity)

    def push(self, experience):
        self.memory.append(experience)

    def sample(self, batch_size):
        return random.sample(self.memory, batch_size)

    def __len__(self):
        return len(self.memory)

# 选择动作的epsilon-greedy策略
def select_action(state, policy_net, epsilon, n_actions):
    if random.random() < epsilon:
        return random.randrange(n_actions)  # 随机探索
    else:
        with torch.no_grad():
            state = torch.tensor(state, dtype=torch.float32).unsqueeze(0).to(device)
            return policy_net(state).argmax(dim=1).item()  # 利用网络选择最优动作

# 更新目标网络
def update_target_network(policy_net, target_net):
    target_net.load_state_dict(policy_net.state_dict())

# 训练 DQN 网络
def optimize_model(policy_net, target_net, memory, optimizer):
    if len(memory) < BATCH_SIZE:
        return
   
    # 从经验回放池中采样
    experiences = memory.sample(BATCH_SIZE)
   
    # 将经验解包为不同部分
    states, actions, rewards, next_states, dones = zip(*experiences)
   
    states = torch.tensor(states, dtype=torch.float32).to(device)
    actions = torch.tensor(actions, dtype=torch.int64).unsqueeze(1).to(device)
    rewards = torch.tensor(rewards, dtype=torch.float32).unsqueeze(1).to(device)
    next_states = torch.tensor(next_states, dtype=torch.float32).to(device)
    dones = torch.tensor(dones, dtype=torch.float32).unsqueeze(1).to(device)
   
    # 当前 Q 值
    q_values = policy_net(states).gather(1, actions)
   
    # 下一个状态的最大 Q 值(目标网络)
    with torch.no_grad():
        max_next_q_values = target_net(next_states).max(1)[0].unsqueeze(1)
        target_q_values = rewards + (GAMMA * max_next_q_values * (1 - dones))

    # 计算损失
    loss = nn.MSELoss()(q_values, target_q_values)
   
    # 反向传播优化
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

# 初始化 DQN 网络和目标网络
n_actions = env.action_space.n
state_size = env.observation_space.shape[0]

policy_net = DQN(state_size, n_actions).to(device)
target_net = DQN(state_size, n_actions).to(device)
update_target_network(policy_net, target_net)

# 优化器
optimizer = optim.Adam(policy_net.parameters(), lr=LEARNING_RATE)

# 经验回放
memory = ReplayMemory(MEMORY_SIZE)

# 训练主循环
epsilon = EPSILON_START
num_episodes = 1000

for episode in range(num_episodes):
    state, _ = env.reset()
    done = False
    total_steps = 0
    total_reward = 0

    while not done:
        # 选择动作
        action = select_action(state, policy_net, epsilon, n_actions)

        # 执行动作
        next_state, reward, done, truncated, _ = env.step(action)
        total_steps += 1
        total_reward += reward

        # 如果游戏结束,则给负奖励
        reward = reward if not done else -10

        # 将经验存入回放池
        memory.push((state, action, reward, next_state, done))

        # 更新状态
        state = next_state

        # 训练模型
        optimize_model(policy_net, target_net, memory, optimizer)

        # 停止训练条件:游戏累计达到5000步,保存模型
        if total_steps >= TRAIN_TARGET_REWARD:
            print(f"Training completed after {episode+1} episodes, reaching {total_steps} steps.")
            torch.save(policy_net.state_dict(), 'dqn_cartpole.pth')
            break

    # 每隔一定步数更新目标网络
    if episode % TARGET_UPDATE == 0:
        update_target_network(policy_net, target_net)

    # epsilon衰减
    epsilon = max(EPSILON_END, EPSILON_DECAY * epsilon)

    print(f"Episode {episode + 1}/{num_episodes},total_steps:{total_steps}, Total Reward: {total_reward}")

    # 检查是否已经达到目标
    if total_steps >= TRAIN_TARGET_REWARD:
        break

env.close()


保存模型的代码:
# 训练结束后保存模型
if total_steps >= TRAIN_TARGET_REWARD:
    print(f"Training completed after {episode+1} episodes, reaching {total_steps} steps.")
    torch.save(policy_net.state_dict(), 'dqn_cartpole.pth')
    break


AI训练.png

模型保存成功,大小6kb

2.使用神经网络控制游戏:
调用神经网络决策关键代码:
state_tensor = torch.tensor(observation, dtype=torch.float32).unsqueeze(0).to(device)
    action = policy_net(state_tensor).argmax(dim=1).item()


使用模型并调用神经网络控制完整代码如下:

import gym
import pygame
import torch
import torch.nn as nn
from pygame.locals import K_LEFT, K_RIGHT, QUIT
import time
import random  # 用于生成随机数

# 设置设备(如果有GPU则使用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 初始化 pygame
pygame.init()

# 设置帧率
FPS = 60
clock = pygame.time.Clock()

# 创建 CartPole 环境,使用 Gym 自带的渲染模式
env = gym.make('CartPole-v1', render_mode="human")

# 重置环境,准备开始游戏
observation, info = env.reset()

# 初始化游戏的运行标志
running = True

# 操作次数
action_count = 0

# 记录游戏开始的时间
start_time = time.time()

# DQN 网络结构
class DQN(nn.Module):
    def __init__(self, state_size, action_size):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(state_size, 24)
        self.fc2 = nn.Linear(24, 24)
        self.fc3 = nn.Linear(24, action_size)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)

# 初始化 DQN 网络和目标网络
n_actions = env.action_space.n
state_size = env.observation_space.shape[0]

# 加载模型并使用神经网络控制游戏
policy_net = DQN(state_size, n_actions).to(device)

# 加载训练好的模型权重
MODEL_PATH = 'dqn_cartpole.pth'
policy_net.load_state_dict(torch.load(MODEL_PATH))

# 设置为评估模式
policy_net.eval()

# 游戏循环
while running:
    # 处理事件队列,检测关闭窗口操作
    for event in pygame.event.get():
        if event.type == QUIT:  # 退出事件
            running = False
    time.sleep(0.3)

    # 获取键盘按键状态
    keys = pygame.key.get_pressed()

    # 默认动作为随机生成 0 或 1,除非检测到按键输入
    if not keys[K_LEFT] and not keys[K_RIGHT]:
        action = random.choice([0, 1])  # 随机选择 0 或 1

    # 根据按键改变动作
    if keys[K_LEFT]:
        print("Left arrow pressed")
        action = 0  # 向左移动
    elif keys[K_RIGHT]:
        print("Right arrow pressed")
        action = 1  # 向右移动
        
    state_tensor = torch.tensor(observation, dtype=torch.float32).unsqueeze(0).to(device)
    action = policy_net(state_tensor).argmax(dim=1).item()

    # 执行动作并更新环境状态
    observation, reward, done, truncated, info = env.step(action)

    # 每执行一次动作,增加操作次数
    action_count += 1

    # 渲染环境,减少渲染频率(每隔 10 帧渲染一次)
    if action_count % 10 == 0:
        env.render()

    # 检查游戏是否结束
    if done or truncated:
        # 记录游戏结束的时间
        end_time = time.time()

        # 计算游戏持续的时间(秒)
        elapsed_time = end_time - start_time

        # 打印游戏信息
        print(f"游戏结束!总共操作了 {action_count} 次,持续时间为 {elapsed_time:.2f} 秒")

        # 重置环境
        observation, info = env.reset()

        # 重置计数器和时间
        action_count = 0
        start_time = time.time()

    # 控制帧率,确保游戏速度合适
    clock.tick(FPS)  # 设置帧率为 60 帧/秒

# 关闭游戏环境和 pygame
env.close()
pygame.quit()

每次都能顺利通关
AI训练.png

参考:
CartPole游戏源码:
https://opendilab.github.io/DI-engine/13_envs/cartpole_zh.html
https://github.com/openai/gym/bl ... control/cartpole.py

使用Reinforce强化学习算法,玩CartPole游戏 #深度学习
https://www.douyin.com/search/%E ... 58&type=general

强化学习之CartPole游戏(Q-learning)   https://blog.csdn.net/level_code/article/details/100852877

如何从零训练神经网络玩游戏?这里有一段详细的解读视频
https://www.leiphone.com/category/ai/LFTGEgM9ScKM1ov2.html
天不生墨翟,万古如长夜!以墨运商,以商助墨。金双石科技长期招聘科技研发人才!微信:qishanxiaolu   电话:15876572365   公司:深圳市金双石科技有限公司
回复

使用道具 举报

 楼主| 崎山小鹿 发表于 2024-10-21 19:07:58 | 显示全部楼层
崎山小鹿
2024-10-21 19:07:58 看全部
更新或重新安装 Visual C++ Redistributable
在 Windows 系统上,torch_python.dll 错误可能与缺失或损坏的 Visual C++ Redistributable 相关。确保安装了最新版本:
前往 Microsoft 网站 下载并安装 Visual C++ Redistributable。
重启计算机并尝试重新运行你的代码。

下载:https://learn.microsoft.com/en-u ... -2017-2019-and-2022
微信图片_20241021191405.png
天不生墨翟,万古如长夜!以墨运商,以商助墨。金双石科技长期招聘科技研发人才!微信:qishanxiaolu   电话:15876572365   公司:深圳市金双石科技有限公司
回复

使用道具 举报

 楼主| 崎山小鹿 发表于 2024-10-22 22:16:14 | 显示全部楼层
崎山小鹿
2024-10-22 22:16:14 看全部
#打印记录
print(f"step = {action_count} action = {action} angle = {observation[2]:.2f} position = {observation[0]:.2f}")
天不生墨翟,万古如长夜!以墨运商,以商助墨。金双石科技长期招聘科技研发人才!微信:qishanxiaolu   电话:15876572365   公司:深圳市金双石科技有限公司
回复

使用道具 举报

 楼主| 崎山小鹿 发表于 2024-10-23 09:02:09 | 显示全部楼层
崎山小鹿
2024-10-23 09:02:09 看全部
应用ReLU函数对数据进行激活,输入为负则置零,为正则不变。

将速度参数替换成加速度参数,重新训练神经网络。
天不生墨翟,万古如长夜!以墨运商,以商助墨。金双石科技长期招聘科技研发人才!微信:qishanxiaolu   电话:15876572365   公司:深圳市金双石科技有限公司
回复

使用道具 举报

 楼主| 崎山小鹿 发表于 2024-10-23 15:28:18 | 显示全部楼层
崎山小鹿
2024-10-23 15:28:18 看全部
下面是实时输出lost的值,并且可以手动停止训练,增加了继续训练和全新训练的功能,
pip install keyboard
完整代码如下:
import os
import gym
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import random
from collections import deque
import keyboard  # 导入keyboard库

# 设置设备(如果有GPU则使用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 创建 CartPole 环境
env = gym.make('CartPole-v1')

# 超参数
GAMMA = 0.99
EPSILON_START = 1.0
EPSILON_END = 0.01
EPSILON_DECAY = 0.995
LEARNING_RATE = 0.001
MEMORY_SIZE = 10000
BATCH_SIZE = 64
TARGET_UPDATE = 10
TRAIN_TARGET_REWARD = 5000
MODEL_PATH = 'dqn_cartpole.pth'  # 模型文件路径

# 定义 DQN 网络
class DQN(nn.Module):
    def __init__(self, state_size, action_size):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(state_size, 24)
        self.fc2 = nn.Linear(24, 24)
        self.fc3 = nn.Linear(24, action_size)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)

# 经验回放池
class ReplayMemory:
    def __init__(self, capacity):
        self.memory = deque(maxlen=capacity)

    def push(self, experience):
        self.memory.append(experience)

    def sample(self, batch_size):
        return random.sample(self.memory, batch_size)

    def __len__(self):
        return len(self.memory)

# 选择动作的epsilon-greedy策略
def select_action(state, policy_net, epsilon, n_actions):
    if random.random() < epsilon:
        return random.randrange(n_actions)
    else:
        with torch.no_grad():
            state = torch.tensor(state, dtype=torch.float32).unsqueeze(0).to(device)
            return policy_net(state).argmax(dim=1).item()

# 更新目标网络
def update_target_network(policy_net, target_net):
    target_net.load_state_dict(policy_net.state_dict())

# 训练 DQN 网络
def optimize_model(policy_net, target_net, memory, optimizer):
    if len(memory) < BATCH_SIZE:
        return None
   
    experiences = memory.sample(BATCH_SIZE)
    states, actions, rewards, next_states, dones = zip(*experiences)

    states = torch.tensor(states, dtype=torch.float32).to(device)
    actions = torch.tensor(actions, dtype=torch.int64).unsqueeze(1).to(device)
    rewards = torch.tensor(rewards, dtype=torch.float32).unsqueeze(1).to(device)
    next_states = torch.tensor(next_states, dtype=torch.float32).to(device)
    dones = torch.tensor(dones, dtype=torch.float32).unsqueeze(1).to(device)

    q_values = policy_net(states).gather(1, actions)

    with torch.no_grad():
        max_next_q_values = target_net(next_states).max(1)[0].unsqueeze(1)
        target_q_values = rewards + (GAMMA * max_next_q_values * (1 - dones))

    loss = nn.MSELoss()(q_values, target_q_values)
   
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
   
    return loss.item()  # 返回损失值

# 初始化 DQN 网络和目标网络
n_actions = env.action_space.n
state_size = env.observation_space.shape[0]

policy_net = DQN(state_size, n_actions).to(device)
target_net = DQN(state_size, n_actions).to(device)
update_target_network(policy_net, target_net)

optimizer = optim.Adam(policy_net.parameters(), lr=LEARNING_RATE)
memory = ReplayMemory(MEMORY_SIZE)

# 检查是否有已保存模型
if os.path.exists(MODEL_PATH):
    choice = input(f"Found saved model at {MODEL_PATH}. Do you want to continue training from this model? (y/n): ")
    if choice.lower() == 'y':
        policy_net.load_state_dict(torch.load(MODEL_PATH))
        print("Loaded model for continued training.")

# 训练主循环
epsilon = EPSILON_START
num_episodes = 1000

for episode in range(num_episodes):
    state, _ = env.reset()
    done = False
    total_steps = 0
    total_reward = 0
    total_loss = 0

    while not done:
        action = select_action(state, policy_net, epsilon, n_actions)
        next_state, reward, done, truncated, _ = env.step(action)
        total_steps += 1
        total_reward += reward

        reward = reward if not done else -10
        memory.push((state, action, reward, next_state, done))

        state = next_state
        loss = optimize_model(policy_net, target_net, memory, optimizer)
        
        # 实时输出
        if loss is not None:
            total_loss += loss
            #print(f"Episode: {episode + 1}/{num_episodes}, Steps: {total_steps}, Loss: {loss:.4f}", end='\r\n')

        # 检查用户输入
        if keyboard.is_pressed('q'):
            print("\nSaving model and stopping training...")
            torch.save(policy_net.state_dict(), MODEL_PATH)
            exit()
        elif keyboard.is_pressed('s'):
            print("\nSaving model...")
            torch.save(policy_net.state_dict(), MODEL_PATH)

        # 检查训练停止条件
        if total_steps >= TRAIN_TARGET_REWARD:
            print(f"Training completed after {episode + 1} episodes, reaching {total_steps} steps.")
            torch.save(policy_net.state_dict(), MODEL_PATH)
            exit()

    # 每隔一定步数更新目标网络
    if episode % TARGET_UPDATE == 0:
        update_target_network(policy_net, target_net)

    epsilon = max(EPSILON_END, EPSILON_DECAY * epsilon)
    v_loss = total_loss/total_steps
    print(f"Episode {episode + 1}/{num_episodes}, Total Reward: {total_reward},Steps: {total_steps},loss:{v_loss:.4f}")


env.close()
天不生墨翟,万古如长夜!以墨运商,以商助墨。金双石科技长期招聘科技研发人才!微信:qishanxiaolu   电话:15876572365   公司:深圳市金双石科技有限公司
回复

使用道具 举报

 楼主| 崎山小鹿 发表于 2024-10-24 12:02:59 | 显示全部楼层
崎山小鹿
2024-10-24 12:02:59 看全部
`torch.relu` 是 PyTorch 中的一个函数,用于对张量(tensor)中的元素逐个应用 **ReLU(Rectified Linear Unit)** 激活函数。ReLU 是一种常见的激活函数,广泛用于神经网络中,因为它简单且有效。

### ReLU 函数的定义:

ReLU 的数学表达式为:

\[
\text{ReLU}(x) = \max(0, x)
\]

也就是说,对于输入的每个元素,如果该值大于 0,则保持原值;如果该值小于或等于 0,则输出 0。

### 作用和意义:
- **线性和非线性之间的平衡**:ReLU 在正区域是线性的,而在负区域则将所有负值归零,这使得它既能引入非线性,又保持简单的计算。
- **梯度消失问题的缓解**:相比其他激活函数(如sigmoid或tanh),ReLU 在正区间导数为 1,这意味着在反向传播过程中能有效传递梯度,从而缓解梯度消失的问题。

### 使用示例:

```python
import torch

# 创建一个包含正数和负数的张量
x = torch.tensor([-1.0, 0.0, 1.0, 2.0])

# 使用 ReLU 函数
y = torch.relu(x)

print(y)
```

输出结果:

```
tensor([0., 0., 1., 2.])
```

在这个例子中,`torch.relu(x)` 将输入张量中的负数转换为 0,而保留正数不变。

### 应用场景:
ReLU 函数经常用于神经网络的隐藏层中,使得模型能够更好地学习复杂的非线性关系。它简单的计算和稀疏激活(很多神经元输出为 0)让其成为深度学习中的默认激活函数之一。

天不生墨翟,万古如长夜!以墨运商,以商助墨。金双石科技长期招聘科技研发人才!微信:qishanxiaolu   电话:15876572365   公司:深圳市金双石科技有限公司
回复

使用道具 举报

  • 您可能感兴趣
您需要登录后才可以回帖 登录 | 立即注册 |

本版积分规则 返回列表

管理员给TA私信
以墨运商,以商助墨。

查看:2906 | 回复:5

  • 在村子中央建立图书馆

    昨天成绩斐然,收获两栋漂亮海滨别墅,和一个高级瞭望塔,但同时损失惨重,村民损失九

    阅读:0|2024-12-22
  • 墨家小镇文化与经济

    文化建设: 墨家十要 旗帜: 八卦 双鱼戏水 经济建设: 麦田、 甘蔗田

    阅读:156|2024-12-20
  • 墨者的面具

    仿照三星堆的面具做头像

    阅读:151|2024-12-19
  • 从出生地前往墨家小镇集合

    装饰一下出生地 现在有路标了,通过路标让会员找到我们的村庄,一路要非常小心不

    阅读:239|2024-12-18
  • 墨家盾牌和武器

    盾牌上有墨家的标志,武器上也有墨家的特色

    阅读:231|2024-12-17
  • 安全的保险箱

    每个人都一个自己的箱子,只有自己能开启。且死亡不掉落! 对着一个上方没有红石导

    阅读:240|2024-12-17
  • 我的世界之墨家旗帜

    如何在我的世界里创建独特的旗帜呢? 将图片生成像素画 https://chuiliu.github.io/d

    阅读:295|2024-12-16
  • 给服务器增加组件

    给服务器增加组件,例如:墨家旗帜 租赁服务器如何使用mod? 答:目前我的世界纯净

    阅读:356|2024-12-15
  • 用手机玩墨山游侠

    用手机玩墨山游侠 各大应用市场都可以下载我的世界游戏,启动之后 选择【开始游戏】

    阅读:366|2024-12-15
  • 墨山游侠服务器开启

    在网易上开启创服之旅 服务器号:25744989 我们先用游戏版本:1.12.2 来测试,看

    阅读:478|2024-12-14
金双石科技,软件开发20年,技术行业领先,您的满意,就是我们的目标,认真负责,开拓进取,让成品物超所值
关于我们
公司简介
发展历程
联系我们
本站站务
友情链接
新手指南
内容审核
商家合作
广告合作
商家入驻
新闻合作

手机APP

官方微博

官方微信

联系电话:15876572365 地址:深圳市宝安区西乡街道宝民二路宝民花园 ( 粤ICP备2021100124号-1 ) 邮箱:qishanxiaolu@qq.com
QQ|Powered by Discuz! X3.5 © 2001-2024 Discuz! Team.
快速回复 返回顶部 返回列表