.output_wrapper pre code { font-family: Consolas, Inconsolata, Courier, monospace; display: block !important; white-space: pre !important; word-wrap: normal !important; word-break: normal !important; overflow: auto !important }
.output_wrapper a:hover { text-decoration: underline; color: rgba(0, 96, 100, 1) }
.output_wrapper figcaption { margin-top: 10px; text-align: center; color: rgba(153, 153, 153, 1); font-size: 0.7em }
.output_wrapper pre code .linenum { padding-right: 20px; word-spacing: 0 }
.task-list-list { list-style-type: none }
.task-list-list.checked { color: rgba(62, 62, 62, 1) }
.task-list-list.uncheck { color: rgba(191, 193, 191, 1) }
.task-list-list .icon_uncheck, .task-list-list .icon_check { display: inline-block; vertical-align: middle; margin-right: 10px }
.task-list-list .icon_check::before { content: “√”; border: 2px solid rgba(62, 62, 62, 1); color: rgba(255, 0, 0, 1) }
.task-list-list .icon_uncheck::before { content: “x”; border: 2px solid rgba(191, 193, 191, 1); color: rgba(191, 193, 191, 1) }
.task-list-list .icon_check::before, .task-list-list .icon_uncheck::before { padding: 2px 8px 2px 5px; border-radius: 5px }
@font-face { font-family: KaTeX_AMS; src: url(“fonts/KaTeX_AMS-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_AMS-Regular.woff”) format(“woff”), url(“fonts/KaTeX_AMS-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@font-face { font-family: KaTeX_Caligraphic; src: url(“fonts/KaTeX_Caligraphic-Bold.woff2”) format(“woff2”), url(“fonts/KaTeX_Caligraphic-Bold.woff”) format(“woff”), url(“fonts/KaTeX_Caligraphic-Bold.ttf”) format(“truetype”); font-weight: bold; font-style: normal }
@font-face { font-family: KaTeX_Caligraphic; src: url(“fonts/KaTeX_Caligraphic-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_Caligraphic-Regular.woff”) format(“woff”), url(“fonts/KaTeX_Caligraphic-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@font-face { font-family: KaTeX_Fraktur; src: url(“fonts/KaTeX_Fraktur-Bold.woff2”) format(“woff2”), url(“fonts/KaTeX_Fraktur-Bold.woff”) format(“woff”), url(“fonts/KaTeX_Fraktur-Bold.ttf”) format(“truetype”); font-weight: bold; font-style: normal }
@font-face { font-family: KaTeX_Fraktur; src: url(“fonts/KaTeX_Fraktur-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_Fraktur-Regular.woff”) format(“woff”), url(“fonts/KaTeX_Fraktur-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@font-face { font-family: KaTeX_Main; src: url(“fonts/KaTeX_Main-Bold.woff2”) format(“woff2”), url(“fonts/KaTeX_Main-Bold.woff”) format(“woff”), url(“fonts/KaTeX_Main-Bold.ttf”) format(“truetype”); font-weight: bold; font-style: normal }
@font-face { font-family: KaTeX_Main; src: url(“fonts/KaTeX_Main-BoldItalic.woff2”) format(“woff2”), url(“fonts/KaTeX_Main-BoldItalic.woff”) format(“woff”), url(“fonts/KaTeX_Main-BoldItalic.ttf”) format(“truetype”); font-weight: bold; font-style: italic }
@font-face { font-family: KaTeX_Main; src: url(“fonts/KaTeX_Main-Italic.woff2”) format(“woff2”), url(“fonts/KaTeX_Main-Italic.woff”) format(“woff”), url(“fonts/KaTeX_Main-Italic.ttf”) format(“truetype”); font-weight: normal; font-style: italic }
@font-face { font-family: KaTeX_Main; src: url(“fonts/KaTeX_Main-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_Main-Regular.woff”) format(“woff”), url(“fonts/KaTeX_Main-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@font-face { font-family: KaTeX_Math; src: url(“fonts/KaTeX_Math-BoldItalic.woff2”) format(“woff2”), url(“fonts/KaTeX_Math-BoldItalic.woff”) format(“woff”), url(“fonts/KaTeX_Math-BoldItalic.ttf”) format(“truetype”); font-weight: bold; font-style: italic }
@font-face { font-family: KaTeX_Math; src: url(“fonts/KaTeX_Math-Italic.woff2”) format(“woff2”), url(“fonts/KaTeX_Math-Italic.woff”) format(“woff”), url(“fonts/KaTeX_Math-Italic.ttf”) format(“truetype”); font-weight: normal; font-style: italic }
@font-face { font-family: KaTeX_SansSerif; src: url(“fonts/KaTeX_SansSerif-Bold.woff2”) format(“woff2”), url(“fonts/KaTeX_SansSerif-Bold.woff”) format(“woff”), url(“fonts/KaTeX_SansSerif-Bold.ttf”) format(“truetype”); font-weight: bold; font-style: normal }
@font-face { font-family: KaTeX_SansSerif; src: url(“fonts/KaTeX_SansSerif-Italic.woff2”) format(“woff2”), url(“fonts/KaTeX_SansSerif-Italic.woff”) format(“woff”), url(“fonts/KaTeX_SansSerif-Italic.ttf”) format(“truetype”); font-weight: normal; font-style: italic }
@font-face { font-family: KaTeX_SansSerif; src: url(“fonts/KaTeX_SansSerif-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_SansSerif-Regular.woff”) format(“woff”), url(“fonts/KaTeX_SansSerif-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@font-face { font-family: KaTeX_Script; src: url(“fonts/KaTeX_Script-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_Script-Regular.woff”) format(“woff”), url(“fonts/KaTeX_Script-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@font-face { font-family: KaTeX_Size1; src: url(“fonts/KaTeX_Size1-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_Size1-Regular.woff”) format(“woff”), url(“fonts/KaTeX_Size1-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@font-face { font-family: KaTeX_Size2; src: url(“fonts/KaTeX_Size2-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_Size2-Regular.woff”) format(“woff”), url(“fonts/KaTeX_Size2-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@font-face { font-family: KaTeX_Size3; src: url(“fonts/KaTeX_Size3-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_Size3-Regular.woff”) format(“woff”), url(“fonts/KaTeX_Size3-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@font-face { font-family: KaTeX_Size4; src: url(“fonts/KaTeX_Size4-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_Size4-Regular.woff”) format(“woff”), url(“fonts/KaTeX_Size4-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@font-face { font-family: KaTeX_Typewriter; src: url(“fonts/KaTeX_Typewriter-Regular.woff2”) format(“woff2”), url(“fonts/KaTeX_Typewriter-Regular.woff”) format(“woff”), url(“fonts/KaTeX_Typewriter-Regular.ttf”) format(“truetype”); font-weight: normal; font-style: normal }
@media screen { .katex .mtable .vertical-separator { min-width: 1px } .katex .mfrac .frac-line, .katex .overline .overline-line, .katex .underline .underline-line, .katex .hline, .katex .hdashline, .katex .rule { min-height: 1px } }
.katex-display { display: block; margin: 1em 0; text-align: center }
.katex-display>.katex { display: block; text-align: center; white-space: nowrap }
.katex-display>.katex>.katex-html { display: block }
.katex-display>.katex>.katex-html>.tag { position: absolute; right: 0 }
.katex { font: 1.21em / 1.2 KaTeX_Main, “Times New Roman”, serif; text-indent: 0; text-rendering: auto }
.katex * { }
.katex .katex-mathml { position: absolute; clip: rect(1px, 1px, 1px, 1px); padding: 0; border: 0; height: 1px; width: 1px; overflow: hidden }
.katex .katex-html { }
.katex .katex-html>.newline { display: block }
.katex .base { position: relative; display: inline-block; white-space: nowrap; width: min-content }
.katex .strut { display: inline-block }
.katex .textbf { font-weight: bold }
.katex .textit { font-style: italic }
.katex .textrm { font-family: KaTeX_Main }
.katex .textsf { font-family: KaTeX_SansSerif }
.katex .texttt { font-family: KaTeX_Typewriter }
.katex .mathit { font-family: KaTeX_Math; font-style: italic }
.katex .mathrm { font-style: normal }
.katex .mathbf { font-family: KaTeX_Main; font-weight: bold }
.katex .boldsymbol { font-family: KaTeX_Math; font-weight: bold; font-style: italic }
.katex .amsrm { font-family: KaTeX_AMS }
.katex .mathbb, .katex .textbb { font-family: KaTeX_AMS }
.katex .mathcal { font-family: KaTeX_Caligraphic }
.katex .mathfrak, .katex .textfrak { font-family: KaTeX_Fraktur }
.katex .mathtt { font-family: KaTeX_Typewriter }
.katex .mathscr, .katex .textscr { font-family: KaTeX_Script }
.katex .mathsf, .katex .textsf { font-family: KaTeX_SansSerif }
.katex .mainit { font-family: KaTeX_Main; font-style: italic }
.katex .mainrm { font-family: KaTeX_Main; font-style: normal }
.katex .vlist-t { display: inline-table; table-layout: fixed }
.katex .vlist-r { display: table-row }
.katex .vlist { display: table-cell; vertical-align: bottom; position: relative }
.katex .vlist>span { display: block; height: 0; position: relative }
.katex .vlist>span>span { display: inline-block }
.katex .vlist>span>.pstrut { overflow: hidden; width: 0 }
.katex .vlist-t2 { margin-right: -2px }
.katex .vlist-s { display: table-cell; vertical-align: bottom; font-size: 1px; width: 2px; min-width: 2px }
.katex .msupsub { text-align: left }
.katex .mfrac>span>span { text-align: center }
.katex .mfrac .frac-line { display: inline-block; width: 100%; border-bottom-style: solid }
.katex .mspace { display: inline-block }
.katex .llap, .katex .rlap, .katex .clap { width: 0; position: relative }
.katex .llap>.inner, .katex .rlap>.inner, .katex .clap>.inner { position: absolute }
.katex .llap>.fix, .katex .rlap>.fix, .katex .clap>.fix { display: inline-block }
.katex .llap>.inner { right: 0 }
.katex .rlap>.inner, .katex .clap>.inner { left: 0 }
.katex .clap>.inner>span { margin-left: -50%; margin-right: 50% }
.katex .rule { display: inline-block; border: 0 solid; position: relative }
.katex .overline .overline-line, .katex .underline .underline-line, .katex .hline { display: inline-block; width: 100%; border-bottom-style: solid }
.katex .hdashline { display: inline-block; width: 100%; border-bottom-style: dashed }
.katex .sqrt>.root { margin-left: 0.277778em; margin-right: -0.555556em }
.katex .sizing, .katex .fontsize-ensurer { display: inline-block }
.katex .sizing.reset-size1.size1, .katex .fontsize-ensurer.reset-size1.size1 { font-size: 1em }
.katex .sizing.reset-size1.size2, .katex .fontsize-ensurer.reset-size1.size2 { font-size: 1.2em }
.katex .sizing.reset-size1.size3, .katex .fontsize-ensurer.reset-size1.size3 { font-size: 1.4em }
.katex .sizing.reset-size1.size4, .katex .fontsize-ensurer.reset-size1.size4 { font-size: 1.6em }
.katex .sizing.reset-size1.size5, .katex .fontsize-ensurer.reset-size1.size5 { font-size: 1.8em }
.katex .sizing.reset-size1.size6, .katex .fontsize-ensurer.reset-size1.size6 { font-size: 2em }
.katex .sizing.reset-size1.size7, .katex .fontsize-ensurer.reset-size1.size7 { font-size: 2.4em }
.katex .sizing.reset-size1.size8, .katex .fontsize-ensurer.reset-size1.size8 { font-size: 2.88em }
.katex .sizing.reset-size1.size9, .katex .fontsize-ensurer.reset-size1.size9 { font-size: 3.456em }
.katex .sizing.reset-size1.size10, .katex .fontsize-ensurer.reset-size1.size10 { font-size: 4.148em }
.katex .sizing.reset-size1.size11, .katex .fontsize-ensurer.reset-size1.size11 { font-size: 4.976em }
.katex .sizing.reset-size2.size1, .katex .fontsize-ensurer.reset-size2.size1 { font-size: 0.833333em }
.katex .sizing.reset-size2.size2, .katex .fontsize-ensurer.reset-size2.size2 { font-size: 1em }
.katex .sizing.reset-size2.size3, .katex .fontsize-ensurer.reset-size2.size3 { font-size: 1.16667em }
.katex .sizing.reset-size2.size4, .katex .fontsize-ensurer.reset-size2.size4 { font-size: 1.33333em }
.katex .sizing.reset-size2.size5, .katex .fontsize-ensurer.reset-size2.size5 { font-size: 1.5em }
.katex .sizing.reset-size2.size6, .katex .fontsize-ensurer.reset-size2.size6 { font-size: 1.66667em }
.katex .sizing.reset-size2.size7, .katex .fontsize-ensurer.reset-size2.size7 { font-size: 2em }
.katex .sizing.reset-size2.size8, .katex .fontsize-ensurer.reset-size2.size8 { font-size: 2.4em }
.katex .sizing.reset-size2.size9, .katex .fontsize-ensurer.reset-size2.size9 { font-size: 2.88em }
.katex .sizing.reset-size2.size10, .katex .fontsize-ensurer.reset-size2.size10 { font-size: 3.45667em }
.katex .sizing.reset-size2.size11, .katex .fontsize-ensurer.reset-size2.size11 { font-size: 4.14667em }
.katex .sizing.reset-size3.size1, .katex .fontsize-ensurer.reset-size3.size1 { font-size: 0.714286em }
.katex .sizing.reset-size3.size2, .katex .fontsize-ensurer.reset-size3.size2 { font-size: 0.857143em }
.katex .sizing.reset-size3.size3, .katex .fontsize-ensurer.reset-size3.size3 { font-size: 1em }
.katex .sizing.reset-size3.size4, .katex .fontsize-ensurer.reset-size3.size4 { font-size: 1.14286em }
.katex .sizing.reset-size3.size5, .katex .fontsize-ensurer.reset-size3.size5 { font-size: 1.28571em }
.katex .sizing.reset-size3.size6, .katex .fontsize-ensurer.reset-size3.size6 { font-size: 1.42857em }
.katex .sizing.reset-size3.size7, .katex .fontsize-ensurer.reset-size3.size7 { font-size: 1.71429em }
.katex .sizing.reset-size3.size8, .katex .fontsize-ensurer.reset-size3.size8 { font-size: 2.05714em }
.katex .sizing.reset-size3.size9, .katex .fontsize-ensurer.reset-size3.size9 { font-size: 2.46857em }
.katex .sizing.reset-size3.size10, .katex .fontsize-ensurer.reset-size3.size10 { font-size: 2.96286em }
.katex .sizing.reset-size3.size11, .katex .fontsize-ensurer.reset-size3.size11 { font-size: 3.55429em }
.katex .sizing.reset-size4.size1, .katex .fontsize-ensurer.reset-size4.size1 { font-size: 0.625em }
.katex .sizing.reset-size4.size2, .katex .fontsize-ensurer.reset-size4.size2 { font-size: 0.75em }
.katex .sizing.reset-size4.size3, .katex .fontsize-ensurer.reset-size4.size3 { font-size: 0.875em }
.katex .sizing.reset-size4.size4, .katex .fontsize-ensurer.reset-size4.size4 { font-size: 1em }
.katex .sizing.reset-size4.size5, .katex .fontsize-ensurer.reset-size4.size5 { font-size: 1.125em }
.katex .sizing.reset-size4.size6, .katex .fontsize-ensurer.reset-size4.size6 { font-size: 1.25em }
.katex .sizing.reset-size4.size7, .katex .fontsize-ensurer.reset-size4.size7 { font-size: 1.5em }
.katex .sizing.reset-size4.size8, .katex .fontsize-ensurer.reset-size4.size8 { font-size: 1.8em }
.katex .sizing.reset-size4.size9, .katex .fontsize-ensurer.reset-size4.size9 { font-size: 2.16em }
.katex .sizing.reset-size4.size10, .katex .fontsize-ensurer.reset-size4.size10 { font-size: 2.5925em }
.katex .sizing.reset-size4.size11, .katex .fontsize-ensurer.reset-size4.size11 { font-size: 3.11em }
.katex .sizing.reset-size5.size1, .katex .fontsize-ensurer.reset-size5.size1 { font-size: 0.555556em }
.katex .sizing.reset-size5.size2, .katex .fontsize-ensurer.reset-size5.size2 { font-size: 0.666667em }
.katex .sizing.reset-size5.size3, .katex .fontsize-ensurer.reset-size5.size3 { font-size: 0.777778em }
.katex .sizing.reset-size5.size4, .katex .fontsize-ensurer.reset-size5.size4 { font-size: 0.888889em }
.katex .sizing.reset-size5.size5, .katex .fontsize-ensurer.reset-size5.size5 { font-size: 1em }
.katex .sizing.reset-size5.size6, .katex .fontsize-ensurer.reset-size5.size6 { font-size: 1.11111em }
.katex .sizing.reset-size5.size7, .katex .fontsize-ensurer.reset-size5.size7 { font-size: 1.33333em }
.katex .sizing.reset-size5.size8, .katex .fontsize-ensurer.reset-size5.size8 { font-size: 1.6em }
.katex .sizing.reset-size5.size9, .katex .fontsize-ensurer.reset-size5.size9 { font-size: 1.92em }
.katex .sizing.reset-size5.size10, .katex .fontsize-ensurer.reset-size5.size10 { font-size: 2.30444em }
.katex .sizing.reset-size5.size11, .katex .fontsize-ensurer.reset-size5.size11 { font-size: 2.76444em }
.katex .sizing.reset-size6.size1, .katex .fontsize-ensurer.reset-size6.size1 { font-size: 0.5em }
.katex .sizing.reset-size6.size2, .katex .fontsize-ensurer.reset-size6.size2 { font-size: 0.6em }
.katex .sizing.reset-size6.size3, .katex .fontsize-ensurer.reset-size6.size3 { font-size: 0.7em }
.katex .sizing.reset-size6.size4, .katex .fontsize-ensurer.reset-size6.size4 { font-size: 0.8em }
.katex .sizing.reset-size6.size5, .katex .fontsize-ensurer.reset-size6.size5 { font-size: 0.9em }
.katex .sizing.reset-size6.size6, .katex .fontsize-ensurer.reset-size6.size6 { font-size: 1em }
.katex .sizing.reset-size6.size7, .katex .fontsize-ensurer.reset-size6.size7 { font-size: 1.2em }
.katex .sizing.reset-size6.size8, .katex .fontsize-ensurer.reset-size6.size8 { font-size: 1.44em }
.katex .sizing.reset-size6.size9, .katex .fontsize-ensurer.reset-size6.size9 { font-size: 1.728em }
.katex .sizing.reset-size6.size10, .katex .fontsize-ensurer.reset-size6.size10 { font-size: 2.074em }
.katex .sizing.reset-size6.size11, .katex .fontsize-ensurer.reset-size6.size11 { font-size: 2.488em }
.katex .sizing.reset-size7.size1, .katex .fontsize-ensurer.reset-size7.size1 { font-size: 0.416667em }
.katex .sizing.reset-size7.size2, .katex .fontsize-ensurer.reset-size7.size2 { font-size: 0.5em }
.katex .sizing.reset-size7.size3, .katex .fontsize-ensurer.reset-size7.size3 { font-size: 0.583333em }
.katex .sizing.reset-size7.size4, .katex .fontsize-ensurer.reset-size7.size4 { font-size: 0.666667em }
.katex .sizing.reset-size7.size5, .katex .fontsize-ensurer.reset-size7.size5 { font-size: 0.75em }
.katex .sizing.reset-size7.size6, .katex .fontsize-ensurer.reset-size7.size6 { font-size: 0.833333em }
.katex .sizing.reset-size7.size7, .katex .fontsize-ensurer.reset-size7.size7 { font-size: 1em }
.katex .sizing.reset-size7.size8, .katex .fontsize-ensurer.reset-size7.size8 { font-size: 1.2em }
.katex .sizing.reset-size7.size9, .katex .fontsize-ensurer.reset-size7.size9 { font-size: 1.44em }
.katex .sizing.reset-size7.size10, .katex .fontsize-ensurer.reset-size7.size10 { font-size: 1.72833em }
.katex .sizing.reset-size7.size11, .katex .fontsize-ensurer.reset-size7.size11 { font-size: 2.07333em }
.katex .sizing.reset-size8.size1, .katex .fontsize-ensurer.reset-size8.size1 { font-size: 0.347222em }
.katex .sizing.reset-size8.size2, .katex .fontsize-ensurer.reset-size8.size2 { font-size: 0.416667em }
.katex .sizing.reset-size8.size3, .katex .fontsize-ensurer.reset-size8.size3 { font-size: 0.486111em }
.katex .sizing.reset-size8.size4, .katex .fontsize-ensurer.reset-size8.size4 { font-size: 0.555556em }
.katex .sizing.reset-size8.size5, .katex .fontsize-ensurer.reset-size8.size5 { font-size: 0.625em }
.katex .sizing.reset-size8.size6, .katex .fontsize-ensurer.reset-size8.size6 { font-size: 0.694444em }
.katex .sizing.reset-size8.size7, .katex .fontsize-ensurer.reset-size8.size7 { font-size: 0.833333em }
.katex .sizing.reset-size8.size8, .katex .fontsize-ensurer.reset-size8.size8 { font-size: 1em }
.katex .sizing.reset-size8.size9, .katex .fontsize-ensurer.reset-size8.size9 { font-size: 1.2em }
.katex .sizing.reset-size8.size10, .katex .fontsize-ensurer.reset-size8.size10 { font-size: 1.44028em }
.katex .sizing.reset-size8.size11, .katex .fontsize-ensurer.reset-size8.size11 { font-size: 1.72778em }
.katex .sizing.reset-size9.size1, .katex .fontsize-ensurer.reset-size9.size1 { font-size: 0.289352em }
.katex .sizing.reset-size9.size2, .katex .fontsize-ensurer.reset-size9.size2 { font-size: 0.347222em }
.katex .sizing.reset-size9.size3, .katex .fontsize-ensurer.reset-size9.size3 { font-size: 0.405093em }
.katex .sizing.reset-size9.size4, .katex .fontsize-ensurer.reset-size9.size4 { font-size: 0.462963em }
.katex .sizing.reset-size9.size5, .katex .fontsize-ensurer.reset-size9.size5 { font-size: 0.520833em }
.katex .sizing.reset-size9.size6, .katex .fontsize-ensurer.reset-size9.size6 { font-size: 0.578704em }
.katex .sizing.reset-size9.size7, .katex .fontsize-ensurer.reset-size9.size7 { font-size: 0.694444em }
.katex .sizing.reset-size9.size8, .katex .fontsize-ensurer.reset-size9.size8 { font-size: 0.833333em }
.katex .sizing.reset-size9.size9, .katex .fontsize-ensurer.reset-size9.size9 { font-size: 1em }
.katex .sizing.reset-size9.size10, .katex .fontsize-ensurer.reset-size9.size10 { font-size: 1.20023em }
.katex .sizing.reset-size9.size11, .katex .fontsize-ensurer.reset-size9.size11 { font-size: 1.43981em }
.katex .sizing.reset-size10.size1, .katex .fontsize-ensurer.reset-size10.size1 { font-size: 0.24108em }
.katex .sizing.reset-size10.size2, .katex .fontsize-ensurer.reset-size10.size2 { font-size: 0.289296em }
.katex .sizing.reset-size10.size3, .katex .fontsize-ensurer.reset-size10.size3 { font-size: 0.337512em }
.katex .sizing.reset-size10.size4, .katex .fontsize-ensurer.reset-size10.size4 { font-size: 0.385728em }
.katex .sizing.reset-size10.size5, .katex .fontsize-ensurer.reset-size10.size5 { font-size: 0.433944em }
.katex .sizing.reset-size10.size6, .katex .fontsize-ensurer.reset-size10.size6 { font-size: 0.48216em }
.katex .sizing.reset-size10.size7, .katex .fontsize-ensurer.reset-size10.size7 { font-size: 0.578592em }
.katex .sizing.reset-size10.size8, .katex .fontsize-ensurer.reset-size10.size8 { font-size: 0.694311em }
.katex .sizing.reset-size10.size9, .katex .fontsize-ensurer.reset-size10.size9 { font-size: 0.833173em }
.katex .sizing.reset-size10.size10, .katex .fontsize-ensurer.reset-size10.size10 { font-size: 1em }
.katex .sizing.reset-size10.size11, .katex .fontsize-ensurer.reset-size10.size11 { font-size: 1.19961em }
.katex .sizing.reset-size11.size1, .katex .fontsize-ensurer.reset-size11.size1 { font-size: 0.200965em }
.katex .sizing.reset-size11.size2, .katex .fontsize-ensurer.reset-size11.size2 { font-size: 0.241158em }
.katex .sizing.reset-size11.size3, .katex .fontsize-ensurer.reset-size11.size3 { font-size: 0.28135em }
.katex .sizing.reset-size11.size4, .katex .fontsize-ensurer.reset-size11.size4 { font-size: 0.321543em }
.katex .sizing.reset-size11.size5, .katex .fontsize-ensurer.reset-size11.size5 { font-size: 0.361736em }
.katex .sizing.reset-size11.size6, .katex .fontsize-ensurer.reset-size11.size6 { font-size: 0.401929em }
.katex .sizing.reset-size11.size7, .katex .fontsize-ensurer.reset-size11.size7 { font-size: 0.482315em }
.katex .sizing.reset-size11.size8, .katex .fontsize-ensurer.reset-size11.size8 { font-size: 0.578778em }
.katex .sizing.reset-size11.size9, .katex .fontsize-ensurer.reset-size11.size9 { font-size: 0.694534em }
.katex .sizing.reset-size11.size10, .katex .fontsize-ensurer.reset-size11.size10 { font-size: 0.833601em }
.katex .sizing.reset-size11.size11, .katex .fontsize-ensurer.reset-size11.size11 { font-size: 1em }
.katex .delimsizing.size1 { font-family: KaTeX_Size1 }
.katex .delimsizing.size2 { font-family: KaTeX_Size2 }
.katex .delimsizing.size3 { font-family: KaTeX_Size3 }
.katex .delimsizing.size4 { font-family: KaTeX_Size4 }
.katex .delimsizing.mult .delim-size1>span { font-family: KaTeX_Size1 }
.katex .delimsizing.mult .delim-size4>span { font-family: KaTeX_Size4 }
.katex .nulldelimiter { display: inline-block; width: 0.12em }
.katex .delimcenter { position: relative }
.katex .op-symbol { position: relative }
.katex .op-symbol.small-op { font-family: KaTeX_Size1 }
.katex .op-symbol.large-op { font-family: KaTeX_Size2 }
.katex .op-limits>.vlist-t { text-align: center }
.katex .accent>.vlist-t { text-align: center }
.katex .accent .accent-body:not(.accent-full) { width: 0 }
.katex .accent .accent-body { position: relative }
.katex .overlay { display: block }
.katex .mtable .vertical-separator { display: inline-block; margin: 0 -0.025em; border-right: 0.05em solid }
.katex .mtable .vs-dashed { border-right: 0.05em dashed }
.katex .mtable .arraycolsep { display: inline-block }
.katex .mtable .col-align-c>.vlist-t { text-align: center }
.katex .mtable .col-align-l>.vlist-t { text-align: left }
.katex .mtable .col-align-r>.vlist-t { text-align: right }
.katex .svg-align { text-align: left }
.katex svg, .screenShotTempCanvas { display: block; position: absolute; width: 100%; height: inherit; fill: currentColor; stroke: currentColor; fill-rule: nonzero; fill-opacity: 1; stroke-width: 1; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0; stroke-opacity: 1 }
.katex svg path { stroke: none }
.katex .stretchy { width: 100%; display: block; position: relative; overflow: hidden }
.katex .stretchy::before, .katex .stretchy::after { content: “” }
.katex .hide-tail { width: 100%; position: relative; overflow: hidden }
.katex .halfarrow-left { position: absolute; left: 0; width: 50.2%; overflow: hidden }
.katex .halfarrow-right { position: absolute; right: 0; width: 50.2%; overflow: hidden }
.katex .brace-left { position: absolute; left: 0; width: 25.1%; overflow: hidden }
.katex .brace-center { position: absolute; left: 25%; width: 50%; overflow: hidden }
.katex .brace-right { position: absolute; right: 0; width: 25.1%; overflow: hidden }
.katex .x-arrow-pad { padding: 0 0.5em }
.katex .x-arrow, .katex .mover, .katex .munder { text-align: center }
.katex .boxpad { padding: 0 0.3em }
.katex .fbox { box-sizing: border-box; border: 0.04em solid rgba(0, 0, 0, 1) }
.katex .fcolorbox { box-sizing: border-box; border: 0.04em solid }
.katex .cancel-pad { padding: 0 0.2em }
.katex .cancel-lap { margin-left: -0.2em; margin-right: -0.2em }
.katex .sout { border-bottom-style: solid; border-bottom-width: 0.08em }
.output_wrapper pre code { }
.output_wrapper .hljs { color: rgba(169, 183, 198, 1); background: rgba(40, 43, 46, 1); display: block; overflow-x: auto; padding: 0.5em }
.output_wrapper .hljs-params { color: rgba(255, 152, 35, 1) }
.output_wrapper .hljs-number, .output_wrapper .hljs-literal, .output_wrapper .hljs-symbol, .output_wrapper .hljs-bullet { color: rgba(174, 135, 250, 1) }
.output_wrapper .hljs-function, .output_wrapper .hljs-built_in, .output_wrapper .hljs-name, .output_wrapper .hljs-keyword, .output_wrapper .hljs-selector-tag, .output_wrapper .hljs-deletion { color: rgba(248, 35, 117, 1) }
.output_wrapper .hljs-variable, .output_wrapper .hljs-template-variable, .output_wrapper .hljs-link { color: rgba(98, 151, 85, 1) }
.output_wrapper .hljs-comment, .output_wrapper .hljs-quote { color: rgba(128, 128, 128, 1) }
.output_wrapper .hljs-meta { color: rgba(91, 218, 237, 1) }
.output_wrapper .hljs-string, .output_wrapper .hljs-attribute, .output_wrapper .hljs-addition { color: rgba(238, 220, 112, 1) }
.output_wrapper .hljs-attr, .output_wrapper .hljs-section, .output_wrapper .hljs-title, .output_wrapper .hljs-type { color: rgba(165, 218, 45, 1) }
.output_wrapper .hljs-selector-class { color: rgba(165, 218, 45, 1) }
.output_wrapper .hljs-emphasis { font-style: italic }
.output_wrapper .hljs-strong { font-weight: bold }
.output_wrapper pre code { line-height: 18px; font-size: 14px; font-weight: normal; word-spacing: 0; letter-spacing: 0 }
.output_wrapper { font-size: 16px; color: rgba(62, 62, 62, 1); line-height: 1.6; word-spacing: 0; letter-spacing: 0; font-family: “Helvetica Neue”, Helvetica, “Hiragino Sans GB”, “Microsoft YaHei”, Arial, sans-serif; background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%); background-size: 20px 20px; background-position: center }
.output_wrapper * { font-size: inherit; color: inherit; line-height: inherit; margin: 0; padding: 0 }
.output_wrapper p { margin: 1.5em 0 }
.output_wrapper h1, .output_wrapper h2, .output_wrapper h3, .output_wrapper h4, .output_wrapper h5, .output_wrapper h6 { margin: 1.5em 0; font-weight: bold }
.output_wrapper h1 { font-size: 1.6em }
.output_wrapper h2 { font-size: 1.4em }
.output_wrapper h3 { font-size: 1.3em }
.output_wrapper h4 { font-size: 1.2em }
.output_wrapper h5 { font-size: 1em }
.output_wrapper h6 { font-size: 1em }
.output_wrapper ul, .output_wrapper ol { padding-left: 32px }
.output_wrapper ul { list-style-type: disc }
.output_wrapper ol { list-style-type: decimal }
.output_wrapper li * { }
.output_wrapper li { margin-bottom: 0.5em }
.output_wrapper .code_size_default { line-height: 18px; font-size: 14px; font-weight: normal; word-spacing: 0; letter-spacing: 0 }
.output_wrapper .code_size_tight { line-height: 15px; font-size: 11px; font-weight: normal; word-spacing: -3px; letter-spacing: 0 }
.output_wrapper pre code { font-family: Consolas, Inconsolata, Courier, monospace; border-radius: 0 }
.output_wrapper blockquote { display: block; padding: 15px 15px 15px 1rem; font-size: 0.9em; margin: 1em 0; color: rgba(129, 145, 152, 1); border-left: 6px solid rgba(220, 230, 240, 1); background: rgba(242, 247, 251, 1); overflow: auto; overflow-wrap: normal; word-break: normal }
.output_wrapper blockquote p { margin: 0 }
.output_wrapper a { text-decoration: none; color: rgba(30, 107, 184, 1); overflow-wrap: break-word }
.output_wrapper strong { font-weight: bold }
.output_wrapper em { font-style: italic }
.output_wrapper del { font-style: italic }
.output_wrapper strong em { font-weight: bold }
.output_wrapper hr { height: 1px; margin: 1.5rem 0; border-right: none; border-bottom: none; border-left: none; border-top: 1px dashed rgba(165, 165, 165, 1) }
.output_wrapper code { overflow-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: rgba(233, 105, 0, 1); background: rgba(248, 248, 248, 1) }
.output_wrapper img { display: block; margin: 0 auto; max-width: 100% }
.output_wrapper figcaption { margin-top: 10px; text-align: center; color: rgba(153, 153, 153, 1); font-size: 0.7em }
.output_wrapper table { display: table; width: 100%; text-align: left }
.output_wrapper tbody { border: 0 }
.output_wrapper table tr { border-top: 1px solid rgba(204, 204, 204, 1); border-right: 0; border-bottom: 0; border-left: 0; background-color: rgba(255, 255, 255, 1) }
.output_wrapper table tr th, .output_wrapper table tr td { font-size: 1em; border: 1px solid rgba(204, 204, 204, 1); padding: 0.5em 1em; text-align: left }
.output_wrapper table tr th { font-weight: bold; background-color: rgba(240, 240, 240, 1) }
.output_wrapper .katex-display { font-size: 1.22em }
.output_wrapper .katex { padding: 8px 3px }
.output_wrapper .katex-display>.katex { display: inline-block; text-align: center; padding: 3px }
.output_wrapper .katex img { display: inline-block; vertical-align: middle }
.output_wrapper a[href^=”#”] sup { vertical-align: super; margin: 0 2px; padding: 1px 3px; color: rgba(255, 255, 255, 1); background: rgba(102, 102, 102, 1); font-size: 0.7em }
.output_wrapper .task-list-list { list-style-type: none }
.output_wrapper .task-list-list.checked { color: rgba(62, 62, 62, 1) }
.output_wrapper .task-list-list.uncheck { color: rgba(191, 193, 191, 1) }
.output_wrapper .task-list-list .icon_uncheck, .output_wrapper .task-list-list .icon_check { display: inline-block; vertical-align: middle; margin-right: 10px }
.output_wrapper .task-list-list .icon_check::before { content: “√”; border: 2px solid rgba(62, 62, 62, 1); color: rgba(255, 0, 0, 1) }
.output_wrapper .task-list-list .icon_uncheck::before { content: “x”; border: 2px solid rgba(191, 193, 191, 1); color: rgba(191, 193, 191, 1) }
.output_wrapper .task-list-list .icon_check::before, .output_wrapper .task-list-list .icon_uncheck::before { padding: 2px 8px 2px 5px; border-radius: 5px }
.output_wrapper .toc { margin-left: 25px }
.output_wrapper .toc_item { display: block }
.output_wrapper .toc_left { margin-left: 25px }
.output_wrapper pre code { border-radius: 3px; border-top: 1px solid rgba(204, 204, 204, 1); border-right: 1px solid rgba(204, 204, 204, 1); border-bottom: 1px solid rgba(204, 204, 204, 1); border-left: 6px solid rgba(33, 152, 99, 1) }
.output_wrapper pre code .linenum { padding-right: 20px; word-spacing: 0 }
.output_wrapper .hljs { color: rgba(169, 183, 198, 1); background: rgba(40, 43, 46, 1); display: block; overflow-x: auto; padding: 0.5em }
.output_wrapper .hljs-params { color: rgba(255, 152, 35, 1) }
.output_wrapper .hljs-number, .output_wrapper .hljs-literal, .output_wrapper .hljs-symbol, .output_wrapper .hljs-bullet { color: rgba(174, 135, 250, 1) }
.output_wrapper .hljs-function, .output_wrapper .hljs-built_in, .output_wrapper .hljs-name, .output_wrapper .hljs-keyword, .output_wrapper .hljs-selector-tag, .output_wrapper .hljs-deletion { color: rgba(248, 35, 117, 1) }
.output_wrapper .hljs-variable, .output_wrapper .hljs-template-variable, .output_wrapper .hljs-link { color: rgba(98, 151, 85, 1) }
.output_wrapper .hljs-comment, .output_wrapper .hljs-quote { color: rgba(128, 128, 128, 1) }
.output_wrapper .hljs-meta { color: rgba(91, 218, 237, 1) }
.output_wrapper .hljs-string, .output_wrapper .hljs-attribute, .output_wrapper .hljs-addition { color: rgba(238, 220, 112, 1) }
.output_wrapper .hljs-attr, .output_wrapper .hljs-section, .output_wrapper .hljs-title, .output_wrapper .hljs-type { color: rgba(165, 218, 45, 1) }
.output_wrapper .hljs-selector-class { color: rgba(165, 218, 45, 1) }
.output_wrapper .hljs-emphasis { font-style: italic }
.output_wrapper .hljs-strong { font-weight: bold }
.output_wrapper p { margin: 1.5em 0 }
.output_wrapper h1, .output_wrapper h2, .output_wrapper h3, .output_wrapper h4, .output_wrapper h5, .output_wrapper h6 { margin: 1.5em 0; font-weight: bold }
.output_wrapper h1 { font-size: 1.6em }
.output_wrapper h2 { font-size: 1.4em }
.output_wrapper h3 { font-size: 1.3em }
.output_wrapper h4 { font-size: 1.2em }
.output_wrapper h5 { font-size: 1em }
.output_wrapper h6 { font-size: 1em }
.output_wrapper h3 { border-bottom: 2px solid rgba(62, 62, 62, 1); margin-bottom: 50px }
.output_wrapper h3 span { display: inline-block; padding: 10px 0 }
.output_wrapper h3 span::first-letter, .output_wrapper h3 .firstletter { color: rgba(255, 255, 255, 1); padding: 10px 15px; margin-right: 20px; background: rgba(62, 62, 62, 1) }

原文:Unreal Engine 4 Audio Tutorial
作者:Tommy Tran
译者:Shuchang Liu

在本篇教程中,你将学习如何以不同方法播放3D音效,并通过UI控制音频音量。

在视频游戏中,游戏音频通常指的是音乐,对白和音效。在当前的游戏时代,如果你的游戏项目里没有音频,基本就是未经打磨的半成品。

音频可以增加游戏沉浸感。音乐能激起玩家的情绪反应,对白刻画了角色,推进了故事发展,音效提供了反馈和可信度。这些元素促使一款好游戏变成一款优秀的游戏。

在本篇教程,你将学习:

  • 播放音乐并使之循环
  • 在动画特定节点播放音效
  • 每次播放音频前调整音调
  • 根据3D位置空间调整音量
  • 通过UI分别控制音乐和音效音量

请注意,本篇教程涉及蓝图部分内容。如果你需要复习有关内容,请查看蓝图教程

此外推荐你带上耳机学习实践本篇教程内容,因为其中涉及立体音频的内容。

注意:本篇教程只是Unreal Engine 4系列教程的其中一篇:

起步入门

下载示例项目并解压。进入项目文件夹,双击SkywardMuffin.uproject打开项目。

按下Play可以运行游戏。游戏玩法是尽可能踩着天空云朵往上跳跃,同时避免掉落地面,点击左键跳跃地面。

很解压的小游戏,对吧?为了强化放松解压的感觉,我们首先要播放一些宁静的钢琴曲。

播放音乐

在Content Browser打开Audio文件夹,这里有本篇教程要用到的所有声音资源。你可以通过在鼠标悬浮在图标上并点击播放图标进行试听。

要实现播放音乐非常简单,只要将音频资源拖拽到Viewport就行了。然而,这样音乐只会播放一遍,我们必须手动设置音频资源的循环播放。双击打开S_Music

会弹出一个带有简单Details设置的窗口,在Sound Wave部分设置勾选Looping设置。

接着,回到主编辑器将S_Music拖拽至Viewport。

这样关卡场景就会创建出一个带有S_Music音源的AmbientSound Actor,这个Actor会在游戏运行时播放S_Music

接着,我们要在蘑菇小人每次移动时播放一个音效,要实现这点,我们要使用动画通知(Animation Notify)

什么是动画通知?

动画通知可以在动画的特定节点触发指定事件,我们可以用它在实现很多功能。比如,创建一个动画通知来生成粒子特效。

在游戏中,重玩按钮会在蘑菇小人掉落地面时弹出来,利用动画通知,我们就可以改为在死亡动画结束后再弹出按钮。

在本篇教程中,我们将利用动画通知,实现蘑菇小人每次迈步走路时播放音效。

创建动画通知

打开Characters\Muffin文件夹双击打开SK_Muffin_Walk,可以打开动画编辑器。

在Viewport下面的面板,我们能看到名为Notifies的区域。亮灰色区域称之为通知轨道(Notify Track)。我们可以在这里创建管理通知。

左右脚触地的时刻分别是第10帧第22帧,所以我们要分别在这两个节点上创建通知。在通知轨道上点击右键,从弹出菜单中选择Add Notify\Play Sound,创建名为PlaySound的通知。

接着,我们需要将通知位置调整到第10帧处。

移动动画通知

要想直接将通知移动到第10帧的位置会有点难度,因为我们不知道它的具体位置。然而,我们可以通过在时间轴调整播放进度条的位置来做位置参考。

首先,时间轴处于面板的底部。在暂停动画播放后,拖拽红色的播放进度条Current Frame10的位置。

现在,通知轨道上就会有点红色指示线用于标记当前播放位置。

拖拽PlaySound通知到红色指示线处。

接着,我们要让通知能控制播放脚步声。

播放脚步声

左键选中PlaySound,然后再Details面板的Anim Notify设置处,设置SoundS_Footstep

如法炮制设置另一只脚的脚步声。按照如下操作:

  • 创建另一个Play Sound通知
  • 将通知调整到第21帧位置
  • 将通知的Sound设置成S_Footstep

现在,只要步行动画播放到第10帧第22帧,就会触发通知播放S_Footstep

关闭SK_Muffin_Walk回到主编辑器,按下Play运行游戏,四处走动停下脚步声吧。

仔细听听脚步声,你可以会注意到一些细节。双腿迈步的音效是一模一样的!如果有办法让音效每次播放时都有些变化就好了。

在教程下一部分,我们将实现当角色碰到云朵时播放音效。这一次,我们会使用Sound Cue来动态调整音效。

什么是Sound Cue?

Sound Cue是一种控制多个音效并进行合成播放的资源,我们可以将一个正常的音频资源制作成Sound Cue。项目的每个音效资源,也都能单独包装制作成Sound Cue类型资源来进行播放。

下面例子是用Concatenator节点将3个音频资源合成一个Sound Cue来按顺序播放:

如果改成用Random节点,播放Sound Cue的效果就是从3个音频中随机一个进行播放。

在本篇教程里,我们将创建一个Sound Cue并控制它的音调。

创建Sound Cue

首先,回到Audio文件夹,我们要将S_Pop包装制作成Sound Cue,通过右键点击S_Pop,在弹出菜单中选择Create Cue进行创建。

这样会创建出名为S_Pop_Cue的新资源,双击S_Pop_Cue打开Sound Cue编辑器。

注意:编辑器外观跟材质编辑器外观非常相似,在此不再赘述。如果你还不熟悉材质编辑器,请查看入门教程

在图表中,我们可以看到两个节点:Wave Player:S_PopOutput。Sound Cue会播放任何与Output节点相连的音效(在本例里,它会播放S_Pop)。你可以通过在Toolbar上点击Play Cue来试听。

接着,我们要试着修改它的音调。

修改声音音调

为了修改声音的音调,我们需要用到Modulator节点。创建节点并如下图进行连接:

现在就可以修改音调了。选中Modulator节点,在Details面板可以看到两个相关字段:Pitch MinPitch Max。数值如果低于1,说明音调可以变低,反之亦然。数值为1说明保持声音原本的音调。

在本例中,我们只会调高声音音调,因此将Pitch Min设为1.0Pitch Max设为2.0

现在,每次播放Sound Cue时,声音音调都会随机在原音调和原音调的两倍之间。

播放Sound Cue

回到主编辑器,打开Blueprints文件夹。打开BP_Cloud并点进CloudTouched函数里。该函数在玩家碰到云朵时就会调用,很适合在此时播放Sound Cue。

有以下两种节点都适合用来播放声音:

  • Play Sound 2D:不考虑衰减和空间定位进行声音播放(关于这两个概念等下讲解)。该节点适合用于播放不是从游戏世界中产生的声音,比如背景音乐和UI音效。

  • Play Sound at Location:指定3D空间位置作为音源进行声音播放。该节点适合用于模拟声音从某个位置某个方向传出的效果。

由于云朵是游戏世界里的具体实体,声音理所当然要从实体中传出来才显得逼真。因此我们在节点末端添加Play Sound at Location节点。

随后,将Sound设置为S_Pop_Cue

点击Compile并返回主编辑器,按下Play运行游戏,每次碰到运动,你应该都能听到不同的音调声音。

现在声音有了高低音效之分了,但听起来没有什么立体效果。要实现这点,我们需要将声音空间化(spatialize)

什么是空间定位(Spatialization)?

空间定位是一种让声音听起来处于3D空间的处理手段。声音如果从左边传来,那左边的耳机声音会更清楚,反之亦然。

除了能增强沉浸感,空间定位还能完善游戏玩法。像守望先锋反恐精英等竞技游戏中,立体声效可以让玩家敏锐地感知对手的位置。

在本篇教程中,我们会对某些云朵音效启用空间定位。

启用空间定位

有以下两种方法对Sound Cue启用空间定位:

  • Sound Attenuation资源:这种资源带有衰减和空间定位设置。你可以给不同的声音指定相同的Sound Attenuation资源以确保设置一致。
  • Override Attenuation:除了使用Sound Attenuation资源,你也可以通过Override Attenuation选项设置具体Sound Cue的声音设置。

在本例中,我们将使用第二种方法。打开S_Pop_Cue,在Details面板的Attenuation设置处勾选Override Attenuation,这样可以启用Attenuation Overrides部分设置。

我们可以观察Attenuation Overrides处的箭头是否可点判断空间定位是否启用。Spatialize设置表明声音是否会衰减。

如上图进行设置然后关闭S_Pop_Cue。接着,我们要指定声音的3D空间位置。

在指定3D空间播放声音

打开BP_Cloud并创建GetActorLocation节点,随后将其与Play Sound at Location节点的Location引脚相连。

这样声音就会在云朵的位置播放出来。

点击Compile并返回主编辑器。按下Play运行游戏,操控小人触碰云朵,你可以感觉到声音是从不同的地方传来的。

注意:默认情况下摄像机就是音频监听者(audio listener)。这意味着我们是以摄像机的视角去听那些声音的。如果你想修改监听者对应的Actor,可以使用Set Audio Listener Override节点。

你可能注意到了有些云是会下雨的,如果它没有对应的下雨声那就不真实了!接着,我们要为其添加雨声,并启用衰减(attenuation)来声音产生远近变化。

添加雨声

与其使用节点播放雨声,我们可以使用Audio组件。使用组件的一大优点就是它能在云的位置自动播放。

打开BP_Cloud,在Components面板添加Audio组件,将其命名为RainAudio

选中组件,在Details面板找到Sound设置,将Sound设为S_Rain

普通云朵不应该播放雨声,即普通云朵要禁用RainAudio组件,我们在Details面板找到Activation设置并取消勾选Auto Activate

雨云则要启用RainAudio组件,我们可以在EnableRain函数做这件事,只有雨云实例才会执行该函数。打开EnableRain函数并添加如下高亮节点:

接着,我们需要启用并设置衰减。

设置衰减

在Components面板选中RainAudio,在Details面板找到Attenuation设置,启用Override Attenuation

衰减设置确定了空间距离与声音衰减的比例关系。在Attenuation Overrides设置里,可以修改以下两个参数:

  • Radius(半径):声音开始衰减的最远距离

  • Falloff Distance(衰减距离):声音衰减至静默的距离,这个距离从Radius往外开始算起。

看看如下例子:

当玩家处于内圆半径时(取决于Radius),音量为100%。当玩家从内圆走向外圆边缘时,音量会逐渐衰减至0%。

对于本篇教程,我们将Radius设为300FalloffDistance设为400

这代表玩家在距离300单位内,音量是100%的,当玩家距离逐渐接近700(300 + 400)单位时,音量减至0%。

有时当云朵从屏幕中移除了,但玩家还未离开声音衰减范围内的情况下,声音会被突然切断。为了解决这个问题,我们给音效加上渐隐效果。

渐隐音效

切换到Event Graph并找到FadeOut事件。我们可以通过在My Blueprint面板找到Graphs部分设置,双击EventGraph列表下的FadeOut

在节点末端添加如下高亮节点:

FadeOut节点会在玩家触碰云朵时触发执行,Timeline (FadeTimeline)节点的Alpha字段会在一段时间内输出从1到0。通过这个数值来控制RainAudio渐隐。

注意:你可以双击Timeline节点打开页签查看节点是否能正常工作。想了解更多关于Timelines的内容,请查看官方文档

接着我们还需要干一件事才能听到S_Rain。当声音的音量降到0%时,它会停止播放。由于在游戏中,一开始我们很可能处于声音的衰减距离外,S_Rain自然就会降至0%。当我们再次进入衰减距离内时,由于声音已停止播放,就不会听到任何东西了。

我们可以通过Virtualize when Silent修正这一点,启用该设置可以保证无论音量大小,声音始终处于播放状态。

点击Compile并关闭BP_Cloud,打开Audio文件夹并打开S_Rain,在Sound设置处启用Virtualize when Silent

现在,S_Rain即使处于静音状态也会始终播放。关闭S_Rain并返回主编辑器,按下Play运行游戏,控制小人接近雨声的有效范围内听听效果吧。

在教程最后一部分,我们将会通过Sound Classes(声音类)Sound Mixes(混音)控制声音音量。

Sound Classes(声音类)和Sound Mixes(混音)

用声音类可以简便地将多个声音组织起来。比如,你可以将所有音乐组织成一个声音类,所有音效组织成另一个类。

为了能在游戏过程中调整声音类的参数(音量,音调等),我们可以使用混音。混音本质是一张表格,表格每行的表头就是声音类。每行数据都表示该行声音类的参数调整。

下图说明了一个混音所包含的数据:

使用了上述的混音,Music声音类的所有声音都会以原本音量的一半来播放。Effect的声音则原本音调的两倍进行播放。

现在,让我们来着手创建声音类。

创建声音类

在本篇教程,我们将分别调整声音和音效的音量。这意味着我们需要两个声音类。在Content Browser里,点击Add New并选择Sounds\Sound Class,将其重命名为S_Music_Class

再创建另一个声音类命名为S_Effects_Class

接着,我们需要指定每个声音的声音类。首先对音乐资源进行设置。打开S_Music,在Sound设置处将Sound Class修改为S_Music_Class

做好以上设置后,就关闭S_Music

接着要设置音效资源。与其一个个指定声音类,我们全选以下资源:

  • S_Footstep
  • S_Pop_Cue
  • S_Rain

随后,右键点击选中资源,选中Asset Actions\Bulk Edit via Property Matrix,这样就能打开属性矩阵编辑器并看到以上资源。

通过编辑器我们可以同时编辑选中资源的通用属性。

在Details面板展开Sound分支,点击Sound Class右侧的网格图标。

选择S_Effects_Class,然后关闭属性矩阵编辑器。

就此所有的音频都设置好声音类了,接着,我们要创建混音,并通过蓝图来控制调整它。

创建并调整混音

在Content Browser里,点击Add New并选中Sounds\Sound Mix,将新建混音命名为S_Volume_Mix

我们可以通过滑条控制每个声音类的音量,项目里已经提前创建好了滑条资源,找到UI文件夹并打开WBP_Options

为了实现音量调节,我们需要获取滑条数值并传给混音。我们首先实现音乐音量控制。

将编辑器切换成Graph模式,并在My Blueprints面板找到Variables设置,选中MusicSlider,然后在Details面板点击On Value Changed旁边的按钮

这样图表就会创建一个On Value Changed (MusicSlider)事件,每次滑动滑条时,事件都会触发执行。

现在,我们需要设置S_Volume_Mix里的S_Music_Class声音类的音量。这里要利用Set Sound Mix Class Override节点,该节点允许我们指定一个混音和一个声音类。如果混音里不存在指定声音类,它会自动将其添加进去。如果已存在,则更新对应的声音类。

添加Set Sound Mix Class Override节点,并做如下设置:

  • In Sound Mix Modifier: S_Volume_Mix
  • In Sound Class: S_Music_Class
  • Fade in Time: 0(确保音量修改即时生效)

接着,如下连接节点:

如法炮制设置EffectsSlider,将In Sound Class引脚修改为S_Effects_Class

现在,只要滑条数值发生变动,S_Volume_Mix就会修改相关声音类的音量。

最后我们还要激活对应混音,才能让这一切运作起来。

激活混音

对本例而言(用UI控制音量),最好是在游戏一开始激活混音。这样声音类一开始就能用上混音设置。然而,简单起见,本例中我们还是在控件中激活混音。

创建Event Pre Construct节点,这个节点跟蓝图的Event BeginPlay节点非常相似。

为了激活混音,我们要使用Push Sound Mix Modifier节点,创建对应节点并与Event Pre Construct相连,随后设置In Sound Mix ModifierS_Volume_Mix

这样在WBP_Options生成时就能激活S_Volume_Mix

点击Compile并关闭WBP_Options。按下Play运行游戏,点击M键弹出滑条,调整滑条感受音量的变化吧。

后续学习

你可以在这里下载完整项目。

如你所见,在Unreal Engine 4里使用音频非常简单。如果你想了解更多关于音频系统的内容,点击查阅官方文档。你还可以实现像混响或EQ这样炫酷的效果,记得也关注下即将推出的全新音频引擎

如果你还想继续学习引擎其他内容,点击下篇教程,将讲解有关粒子特效的内容。

 

版权声明:本文为leoin2012原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/leoin2012/p/11713506.html