前一讲: 配置环境及项目创建(详细操作步骤)https://www.acwing.com/blog/content/33045/
项目模块设计
将一个大的模块拆分成一个个小的模块,直到可以这个小模块可以实现为止。
项目
- 菜单界面 menu
- 单人模式
- 多人模式
- 设置
- 对战界面 playground
- 玩家
- 移动
- 释放技能
- 技能 1
- 技能 2
- …
- 时间
- 地图
- 玩家
- 设置界面 settings
项目文件结构
系统目录结构与作用
templates - 存放系统的 html 文件
urls - 存放路由,即请求链接和处理函数的映射关系
views - 存放请求的处理函数逻辑
models - 管理系统的表结构,与数据库相关
static - 存储静态文件
css - 样式文件
js - 操作对象的动作逻辑
image - 图像
audio - 声音
consumers - 管理 websocket 函数
分别在 urls、views、models 目录下创建 __init__.py
文件,之后才能在代码中进行 import 导入使用。
相关配置
在 acapp/acapp/settings.py
文件设置时区:TIME_ZONE = 'Asia/Shanghai'
将自己创建的 game 这个 app 加入到 INSTALLED_APPS
中
game.apps.GameConfig
配置系统静态文件目录(开发),BASE_DIR
是整个项目的根目录
import os
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL = '/static/'
配置用户静态文件目录
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
细化目录结构
在 static 目录创建 css、js、image,分别在每个目录中创建系统三个模块的文件夹进行分类,之后开发不容易混乱
mkdir menu playground settings
其他的目录同理
使用 wget 命令下载一个图片到 image/menu
目录下
wget --output-document=background.gif https://cdn.acwing.com/media/article/image/2023/03/17/1_067a3e9cc4-background.gif
访问 http://IP:8000/static/image/menu/background.gif
就可以看到刚才我们的图片了
这里为啥直接使用 static 就可以访问而不是 game/static 呢?
配置了 STATIC_ROOT 和 STATIC_URL 之后所有的 static 目录都能够识别了,不管你的 static 目录在哪,都能够直接通过 /static/ 进行访问
在 css 目录下创建 game.css touch game.css
,css 文件一般不需要按模块拆分。
js 目录一般分成两个文件夹来写,dist
用于最终提供给前端加载的,通常是对 src 中多个 js 文件的汇总,前端使用时只需加载这一个 js 文件就行了,src
开发时候为了方便通常会按照功能拆分来写,方便维护和调试。
mkdir dist src
在项目根目录创建 scirpts 目录,编写合并 js 的脚本
mkdir scripts
cd scripts
vim compress_game_js.sh
chmod +x compress_game_js.sh # 添加可执行权限
compress_game_js.sh
内容:
#! /bin/bash
JS_PATH=/home/tonngw/acapp/game/static/js/
JS_PATH_DIST=${JS_PATH}dist/
JS_PATH_SRC=${JS_PATH}src/
find ${JS_PATH_SRC} -type f -name '*.js' | sort | xargs cat > ${JS_PATH_DIST}game.js
页面相关
上来先还是创建结构,在 templates
目录下创建三个子目录
mkdir menu playground settings
创建多终端目录,可以支持多个游戏窗口
mkdir multiends
vim web.html
web.html
:
<head>
<!-- 引入第三方的文件 -->
<link rel="stylesheet" href="https://cdn.acwing.com/static/jquery-ui-dist/jquery-ui.min.css">
<script src="https://cdn.acwing.com/static/jquery/js/jquery-3.3.1.min.js"></script>
<!-- 引入自定义的文件 -->
<link rel="stylesheet" href="{% static 'css/game.css' %}">
<script src="{% static 'js/dist/game.js' %}"></script>
</head>
<body style="margin: 0">
<div id="ac_game_12345678"></div>
<script>
$(document).ready(function(){
// 创建游戏界面,可以支持多终端,更多的逻辑在后台 js 文件中实现
let ac_gmae = new AcGame("ac_game_12345678");
});
</script>
</body>
小妙招:在后台创建好 js 文件,然后用户访问页面的时候,把页面返回过去,客户端再请求 js 文件,由客户端完成页面渲染,可以减轻服务器的压力
在 js/src
目录下继续细分,创建 zbase.js
总的 js 文件,z 的含义就是保证字典序最大,因为要引用其他的 js,所以将它放在最后进行代码汇总的时候才能保证正确。
mkdir menu playground settings
vim zbase.js
zbase.js
,JS 通过构造器传入 id 创建对象
class AcGame {
constructor(id) {
}
}
后面我们所有的 HTML 就在 js 文件中写,然后把文件交给客户端进行渲染。
后台逻辑相关
到 views
目录下继续创建三大件,分别创建 __init__.py
创建 index.py
返回首页面
from django.shortcuts import render
def index(request):
return render(request, "multiends/web.html")
来到 urls
目录继续创建三大件,分别创建 __init__.py
和 index.py
比如 urls/menu/index.py
,目前还没有逻辑,其他两个目录的文件内容一样
from django.urls import path
urlpatterns = [
]
总的 urls 配置 urls/index.py
,将其他的 urls 导入(include)进来
from django.urls import path, include
from game.views.index import index
urlpatterns = [
path("", index, name="index"),
path("menu/", include("game.urls.menu.index")),
path("playground/", include("game.urls.playground.index")),
path("settings/", include("game.urls.settings.index")),
]
配置好之后,此时整个项目的目录结构就已经有了一个雏形了,浏览器访问查看控制台可以看到输出说明就成功了!
菜单页面
在 menu/zbase.js
中创建我们的菜单页面
class AcGameMenu { // 创建菜单类
constructor(root) { // 传入父类,即游戏窗口
this.root = root; // 赋值
this.$menu = $(`
<div class="ac-game-menu">
</div>
`); // 创建 html 菜单,加 $ 转为 jquery 对象
this.root.$ac_game.append(this.$menu); // 将创建好的菜单添加到游戏窗口
}
}
css/game.css
文件
.ac-game-menu {
width: 100%;
height: 100%;
background-image: url("/static/image/menu/background.gif");
background-size: 100% 100%; /* 设置背景的宽度和高度,让一张背景图填充满 */
}
src/zbase.js
总的 js 文件
class AcGame {
constructor(id) {
this.id = id; // 指定窗口的 id
this.$ac_game = $('#' + id); // 找到 id 对应的 div,即游戏窗口
this.menu = new AcGameMenu(this); // 创建游戏菜单
}
}
小妙招:
$xxx
,通常在 html DOM 对象前面加$
,方便区分。
完整代码
game.css
,vh 和 vw 单位表示百分比高度和宽度,1vh = 1% height
/* menu */
.ac-game-menu {
width: 100%;
height: 100%;
background-image: url("/static/image/menu/background.gif");
background-size: 100% 100%; /* 设置背景图所占的宽度和高度 */
user-select: none; /* 设置菜单不可选中 */
}
/* menu field */
.ac-game-menu-field {
width: 20vw;
position: relative; /* positive: 表示设置为与父节点的相对位置 */
top: 40vh;
left: 19vw;
}
/* every menu field item */
.ac-game-menu-field-item {
color: white;
height: 7vh;
width: 18vw;
font-size: 6vh;
font-style: italic;
padding: 2vh;
text-align: center;
background-color: rgba(39, 21, 28, 0.6);
border-radius: 10px;
letter-spacing: 0.5vw; /* 每个字之间的距离 */
cursor: pointer; /* 鼠标为小手样式 */
}
/* field item hover style */
.ac-game-menu-field-item:hover {
transform: scale(1.2); /* 移动上去有一个放大缩放的样式 */
transition: 100ms; /* 过渡过程的时间 */
}
menu/zbase.js
class AcGameMenu {
constructor(root) {
this.root = root;
this.$menu = $(`
<div class="ac-game-menu">
<div class="ac-game-menu-field">
<div class="ac-game-menu-field-item ac-game-menu-field-item-single-mode">
单人模式
</div>
<br>
<div class="ac-game-menu-field-item ac-game-menu-field-item-multi-mode">
多人模式
</div>
<br>
<div class="ac-game-menu-field-item ac-game-menu-field-item-settings">
设置
</div>
</div>
</div>
`);
this.root.$ac_game.append(this.$menu);
this.$single_mode = this.$menu.find('.ac-game-menu-field-item-single-mode');
this.$multi_mode = this.$menu.find('.ac-game-menu-field-item-multi-mode');
this.$settings = this.$menu.find('.ac-game-menu-field-item-settings');
this.start();
}
start() {
this.add_listening_events();
}
// 添加监听事件
add_listening_events() {
let outer = this;
this.$single_mode.click(function() {
outer.hide(); // 关闭菜单界面 | hide menu page
outer.root.playground.show(); // 打开游戏界面 | show menu page
});
this.$multi_mode.click(function() {
console.log("click multi mode");
});
this.$settings.click(function() {
console.log("click settings");
});
}
show() { // 展示 menu 页面 | show menu page
this.$menu.show();
}
hide() { // 关闭 menu 界面 | hide menu page
this.$menu.hide();
}
}
playground/zbase.js
class AcGamePlayground {
constructor(root) {
this.root = root;
this.$playground = $(`<div>游戏界面</div>`);
this.hide(); // 刚开始游戏界面是隐藏的 | playground page is hidden at first
this.root.$ac_game.append(this.$playground);
this.start();
}
start() {
}
show() { // 打开 playground 界面 | open playground page
this.$playground.show();
}
hide() { // 关闭 playground 界面 | hide playground page
this.$playground.hide();
}
}
zbase.js
class AcGame {
constructor(id) {
this.id = id;
this.$ac_game = $('#' + id);
this.menu = new AcGameMenu(this); // 创建 menu 对象 | create menu object
this.playground = new AcGamePlayground(this); // 创建 playground 对象 | create playground object
this.start();
}
start() {
}
}
写完记得 Git 同步代码。
兄弟,你
web.html
里的<script>
里有个}
写错了还有
view
目录下index.py
里的`multiends/web.html‘感谢指正,已修改~